基于token的身份验证-2.0版本
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于token的身份验证-2.0版本相关的知识,希望对你有一定的参考价值。
参考技术A 之前写过关于token的文章 Token - 服务端身份验证的流行方案 ,是基于当时的实现方案来写的。后来进行了设计的review,被提出有下面的问题:针对上面的问题,我拉来公司的两位同事进行讨论,将得出的方案作为token2.0版本。
如何做到防止伪造,就是说即使算法泄露,服务端也能识别出来。要做到这一点,服务端就要缓存token的存根,当请求到达时,比较请求中的token和服务端同一个用户的token存根是否匹配。如果匹配,验证通过;如果不匹配,表示token是伪造的;或者账号已经在另外的设备上登录,从而挤掉了当前的token,让用户重新登录。所以这个方案,顺便解决了第3个问题。
服务端缓存token,可以使用redis,以userId为key,以详细用户数据和token为value。收到请求时,需要从token中解析出userId,这样才能进一步根据userId从缓存中查数据。但是有一个问题是如何判断token的有效期。
在前一篇文章中,从token解析出token的创建时间,然后加上有效期,跟系统的当前时间进行比较。如果大于当前时间,表示有效,否则表示过期。但是既然服务端已经把token放入缓存了,可以直接使用缓存的有效期来表示token的有效期。一句话:如果根据token解析出来的userId从缓存中查不出来什么,表示token已经过期。
参考了微信登录,他们是基于OAuth2.0标准来做的。当时也对OAuth2.0进行了调研,结果是它是用来为第三方请求资源时提供的一种授权和身份验证机制。简单来说,它的时序图是下面的样子:
因为我们需要维护自己的用户体系,而且只有一个系统,没有必要搞这么多事,所以最后决定放弃OAuth2.0,自己实现身份验证机制,这其实也是参考了之前的一个项目的做法。
微信登录中提到了两个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端的授权,服务端不应该无限制的发放授权,只有在前一个授权快要过期了,才发一个新的。
基于token的身份验证方法
1、用户向服务器发送用户名和密码
2、服务端收到请求,验证用户名和密码
3、验证成功后,服务端会签发一个Token,再把这个Token发送给客户端。
4、客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local storage里。
5、用户随后的每一次请求,都会通过Cookie,将Token传回服务器。
6、服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据。
有关Token的具体内容待自习理解再做补充,望各位大神指导。
以上是关于基于token的身份验证-2.0版本的主要内容,如果未能解决你的问题,请参考以下文章