一个面向对象的鉴权系统的设计


在学习了极客时间的设计模式之美后,发现了自己由于长时间写curd,导致思维都变得面向过程了。

记录一下整体设计的思路。

要求

关于鉴权系统要求如下:

调用方进行接口请求的时候,将 URL、AppID、密码、时间戳拼接在一起,通过加密算
法生成 token,并且将 token、AppID、时间戳拼接在 URL 中,一并发送到微服务端。

微服务端在接收到调用方的接口请求之后,从请求中拆解出 token、AppID、时间戳。

微服务端首先检查传递过来的时间戳跟当前时间,是否在 token 失效时间窗口内。如果
已经超过失效时间,那就算接口调用鉴权失败,拒绝接口调用请求。

如果 token 验证没有过期失效,微服务端再从自己的存储中,取出 AppID 对应的密
码,通过同样的 token 生成算法,生成另外一个 token,与调用方传递过来的 token 进
行匹配。如果一致,则鉴权成功,允许接口调用;否则就拒绝接口调用。

分析和设计

首先,我们需要分析涉及到的功能

  1. 将 URL、AppID、密码、时间戳拼接在一起,通过加密算法生成 token
  2. 将 token、AppID、时间戳拼接在 URL,形成新的URL
  3. 从请求中拆解出 token、AppID、时间戳
  4. 检查token是否失效
  5. 从存储中取出 AppID 对应的密码
  6. 进行token的验证比较

那么接下来分析涉及到的操作对象主要有三个:

  1. token

    1 4 6

  2. url

    2 3

  3. storage(存储)

    5

那么接下来可以进行相关功能的设计了。

在设计期间,或者直接写代码期间,我的思维是:搭建框架的过程中思考实现

Token设计

token类,可以这样:

public class AuthToken {
    public static String generateToken(String url, String appId, 
                                       String password, Long timestamp);
    public boolean isExpire();
    public boolean match(String token);
}

好的,确实,我们涵盖了所有的功能,但是怎么判断是否过期呢?

显然我们需要一个timestamp变量来记录当前token的时间戳。并且提供一个默认多长时间过期,来判断是否过期了。

public class AuthToken {
    private Long timestamp;
    // 默认过期时间,一分钟
    private static final long DEFAULT_EXPIRED_TIMESTAMP = TimeUnit.MINUTES.toSeconds(1);
    
    public static String generateToken(String url, String appId, 
                                       String password, Long timestamp);
    public boolean isExpire();
    public boolean match(String token);
}

此时如果直接generateToken返回String好像就不太行了,因为我们的token现在包含了除了主体string之外的东西。

改。

public class AuthToken {
    private Long timestamp;
    private String token;
    
    public static AuthToken generateToken(String url, String appId, 
                                       String password, Long timestamp);
    public boolean isExpire();
    public boolean match(AuthToken token);
    public String getToken();
}

具体实现应该就比较简单了,

Url设计
public class URL {
    private String baseUrl;
    private String token;
    private String appId;
    private Long timestamp;
    
    /**
     * 根据参数拼接为url
     */
    public static String generateUrl(String baseUrl, String token, 
                                     String appId, Long timestamp);
    
    /**
     * 根据url构建URL对象,相当于进行URL的解析
     * @param url url
     * @return 构建后的对象
     */
    public static URL createFromUrl(String url);
    
    public String getBaseUrl() {
        return baseUrl;
    }

    public String getToken() {
        return token;
    }

    public String getAppId() {
        return appId;
    }

    public Long getTimestamp() {
        return timestamp;
    }
}
Stroage设计
public interface Storage {
    /**
     * 通过appId获得对应的密码
     * @param appId appId
     * @return 获取到的密码,如果找不到返回null
     */
    public String getPasswordByAppId(String appId);
}
最终提供给其他人调用的第三方调用入口是这样的:
public interface Auth {
    /**
     * 根据url进行权限验证
     * @param url
     * @return
     */
    public void auth(String url);

    /**
     * 根据apiRequest进行权限验证
     * @param url
     * @return
     */
    public void auth(ApiRequest url);
}

可以提供默认的实现:

public class DefaultAuth implements Auth{
    private Storage storage;

    public DefaultAuth() {
        storage = new MySqlStorage();
    }

    public DefaultAuth(Storage storage) {
        this.storage = storage;
    }

    @Override
    public void auth(String url) {
        // 从url构建对应的request对象
        URL url= URL.createFromUrl(url);
        auth(url);
    }

    @Override
    public void auth(URL url) {
        // 构建token对象
        AuthToken authToken = new AuthToken(url.getToken(), url.getTimestamp());
        // 检查是否过期
        if (authToken.isExpired()) {
            throw new TokenExpiredRuntimeException();
        }
        // 根据appid去数据库拿到对应的密码
        String password = storage.getPasswordByAppId(url.getToken());
        // 重新构建服务器端的token
        AuthToken token = AuthToken.create(url.getBaseUrl(), url.getAppId(),
                password, url.getTimestamp());

        // 比较两个token是否相同
        if (authToken.match(token)) {
            throw new TokenNotMatchRuntimeException();
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值