基于token的身份验证-2.0版本

基于token的身份验证-2.0版本,第1张

之前写过关于token的文章 Token - 服务端身份验证的流行方案 ,是基于当时的实现方案来写的。后来进行了设计的review,被提出有下面的问题:

针对上面的问题,我拉来公司的两位同事进行讨论,将得出的方案作为token20版本。

如何做到防止伪造,就是说即使算法泄露,服务端也能识别出来。要做到这一点,服务端就要缓存token的存根,当请求到达时,比较请求中的token和服务端同一个用户的token存根是否匹配。如果匹配,验证通过;如果不匹配,表示token是伪造的;或者账号已经在另外的设备上登录,从而挤掉了当前的token,让用户重新登录。所以这个方案,顺便解决了第3个问题。

服务端缓存token,可以使用redis,以userId为key,以详细用户数据和token为value。收到请求时,需要从token中解析出userId,这样才能进一步根据userId从缓存中查数据。但是有一个问题是如何判断token的有效期。

在前一篇文章中,从token解析出token的创建时间,然后加上有效期,跟系统的当前时间进行比较。如果大于当前时间,表示有效,否则表示过期。但是既然服务端已经把token放入缓存了,可以直接使用缓存的有效期来表示token的有效期。一句话:如果根据token解析出来的userId从缓存中查不出来什么,表示token已经过期。

参考了微信登录,他们是基于OAuth20标准来做的。当时也对OAuth20进行了调研,结果是它是用来为第三方请求资源时提供的一种授权和身份验证机制。简单来说,它的时序图是下面的样子:

因为我们需要维护自己的用户体系,而且只有一个系统,没有必要搞这么多事,所以最后决定放弃OAuth20,自己实现身份验证机制,这其实也是参考了之前的一个项目的做法。

微信登录中提到了两个token:access_token和refresh_token。access_token用来请求业务的时候进行身份验证。当access_token过期时,可以使用refresh_token来刷新token(即请求新的token),直到refresh_token也过期了,那么第三方应用需要重新请求授权。access_token有较短的有效期,一般2个小时,refresh_token有效期较长,有30天。

映射到我们的需求里,就是APP登录的时候,生成access_token和refresh_token,一同返回给APP。当access_token失效时,APP使用refresh_token来请求刷新token。如果refresh_token过期,需要用户重新登录。这里提到的refresh_token,和上文中所说的摘要是同一个作用。

这个方案用来解决第2个问题,但是针对refresh_token的有效期的实现,我是有点纠结的。如果像access_token一样,使用服务器缓存来控制refresh_token的有效期,redis需要缓存一个月的时间,注意是每一个用户。

所以我选择了另外的方案,即对于refresh_token,我们采用之前的方案。根据解析出来的创建时间,加上配置的有效期,跟当前时间做对比。从而判断是否已经过期。

这样又会有前面提到的问题,如果refresh_token的算法泄漏,那么别人可以自己生成refresh_token来做任何事。面对这个问题,实现机制在刷新token时会验证token的缓存是否依然存在,如果存在就拒绝刷新token。但是在用户2个小时没有请求的情况下,其access_token已经从缓存中移除,这个时机就不能防止伪造的refresh_token了。所以我们只是缓解了这个问题,并没有真正解决它。

针对这个问题,我们有另外的一些想法:

这段话说服我了,当前的工作还有很多,这一块不是最紧急的,暂时就这样吧。我总结下关于refresh_token的两种做法,供读者们根据自己的情况选择:

用户在注册、登录之后,服务器根据userId、devicecode [1] 来生成accessToken和refreshToken,其中accessToken放入缓存中,以userId为key。服务端返回accessToken和refreshToken给app。

[1] devicecode是设备指纹,由app端提供。作为生成token的干扰码的一部分,另外一部分是服务端的配置项。

在拦截器中拦截,从请求数据中读取accessToken和devicecode,解密出userId。对于之后的几种状态,做一下解释:

通过验证的进行业务处理,否则返回拒绝及拒绝的原因给app。

还有一点值得一提,服务端提供的接口中,有需要登录的,有不需要登录的,还有一种是可选的,即如果登录了能返回更详细的信息;否则只返回基本的信息。比如说排行榜,如果用户没有登录,那就是返回简单的排行榜;如果用户已经登录了,服务端还会告诉你排行榜中哪一个是你。

所以token的身份验证,拆分到了两个拦截器,一个拦截所有请求,负责解析token;一个拦截需要登录的接口,拒绝未登录用户的请求:

accessToken解析拦截器的设计思想:

登录拦截器的设计思想:

服务端从请求中读取refreshToken和devicecode,解密出userId。

解密失败,直接返回失败信息。

解密成功,但是根据userId可以从缓存中读到accessToken,表示accessToken还未过期,不应该请求刷新token,所以也会直接拒绝。

解密成功,但是refreshToken已经过期,拒绝。

ok的话就生产token,其中accessToken是一定重新生成的,但是如果refreshToken不是即将过期的情况下,是直接返回同一个refreshToken,而不是重新生成。这是为了避免服务端产生过多的有效的refreshToken,如果refreshToken相当于对app端的授权,服务端不应该无限制的发放授权,只有在前一个授权快要过期了,才发一个新的。

config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
其中主要获取signature这个参数,官方文档地址 >access token 存在哪里主要看你的业务需求。大多数oauth授权服务器都支持缓存token。也就是说即使你不保存它,重复请求授权的时候,oauth授权服务器会将缓存中的token返回给你。
如果这种方法行不通的时候,可以考虑放到数据库中,
token和用户id一一对应的,所以可以在user表中增加token的字段进行保存。
当然也可以新建一个 userid -token的对应表。

获取access
token接口有什么用
首先在接口类型处选择逗基础支持地项
在接口列表中选择逗获取access_token接口"项
再输入appid的值,这个值可以在测试号或者服务号页面找到
然后输入secret值,这个值和appid是一起使用的
然后点地检查问题逗即可发送请求到服务器
成功时返回access_token值,这个值在以后的接口调试中要用得到的,记得记下来


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/13447006.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-08-08
下一篇 2023-08-08

发表评论

登录后才能评论

评论列表(0条)

保存