token机制

何为token

​ token即为令牌,是服务器生成的一串字符串,作为客户端向服务器进行请求的“通行证”。在客户端进行初次登陆后由服务器返回,之后的每次请求只需要携带token进行请求即可,而无需携带密码等敏感信息

为何token

  • token可以减少敏感信息在网络间的传递
  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用
  • JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。它不需要在服务端保存会话信息, 所以它易于应用的扩展

基于token的登录流程

token登录流程图

基于token的请求流程

token请求流程图

token的结构

先来看看生成的jwt token的样子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTA1NDcyMzQsImRhdGEiOiJkYXRhIiwiaWF0IjoxNjEwNTQwMDM0fQ.Qno5UbhzAvlN6QAXpbqpOkeTMt4qEQvmY50Yh87JD74

token分为3个部分,每个部分由一个字符 “ . ”相连:

  1. 第一部分(头部/header):

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    

    头部与http的头部差不多作用,是用来存放声明信息的,其主要有:

    {
    	"typ": "JWT",   // 这里声明了类型,即JWT
    	"alg": "HS256"  // 这里声明了加密算法,即HS256加密算法
    }
    

    在头部信息确定好之后,将这些信息经过base64加密,构成了如上所示的token第一部分

  2. 第二部分(载荷/payload):

    eyJleHAiOjE2MTA1NDcyMzQsImRhdGEiOiJkYXRhIiwiaWF0IjoxNjEwNTQwMDM0fQ
    

    载荷同http的body差不多功能,用来存放有效信息的,其中包含的信息字段种类及其含义如下:

    {
        exp,    // 过期时间,这个过期时间必须要大于签发时间
        iat,    // 签发时间
        [data,] // 如果你想的话,可以塞一些非敏感信息
    	[iss,]  // 签发者
    	[sub,]  // 面向的用户
    	[aud,]  // 接收的一方
    	[nbf,]  // 定义一个时间,即在该时间之前,这个jwt是不可用状态
    	[jti]   // 唯一身份标识,主要用来作为一次性token,从而回避重放攻击
    }
    

    在载荷的信息确定好之后,将这些信息经过base64加密,构成了如上所示的token第二部分

  3. 第三部分(签证/signatrue):

    Qno5UbhzAvlN6QAXpbqpOkeTMt4qEQvmY50Yh87JD74
    

    签证信息由3个部分组成:

    • 上文所述加密后的token第一部分
    • 上文所述加密后的token第二部分
    • 自定义的混淆字符串

    上述3个部分经过指定算法加密之后输出的字符串就构成了如上所示的token第三部分

    通俗的来讲就是第三部分是个大杂烩,把上述3个部分丢到加密算法这个大锅里煮(加密),煮出来的菜就是第三部分

    自定义的混淆字符串是很重要的部分,需要好好保存在服务端,生成与验证token就靠它了

token注意事项

  • 由于token的组成关系,前2部分只用了base64这种可以随意解密的东西加密的,而第三部分作为凭证留在了客户端,因此,token里不能存储敏感信息

双token验证机制

场景设置

​ 在基于token验证登录态这个情境下,可以想象一个场景,你在使用app或者在网页上进行操作时,你的token突然就过期了,然后只好被迫停止现在正在进行的操作跳转到登录页进行重新登录操作,这就非常的智熄了,这带给用户,特别是经常使用或正在进行某个操作的用户,一种非常不好的体验。这就是单token验证登录的一个缺点。因此对于经常/正在使用或经常/正在进行操作的用户(我称之为活跃用户)就不应当跳转到登录页面进行登陆操作(除了某些敏感系统或包含敏感信息进行敏感操作的网页、app、系统),对于这个缺点的解决,这里介绍一个双token的验证机制

​ ps:或许会有人说可以把token的有效期设的很长啊,但是记住,千万别那么做,这会让token的安全性变得无法保证,在我的理解中,token本就是为了减少未经保护的敏感信息在网络中的传递而设置的,同时也方便了登录态的检验,但是如果token设置的过期时间相当的长,那么token和帐号的安全性变得几乎没有意义,只要任何一个人拿到了token,就可以在一个相当长的时间内对你的帐号动手动脚(怎么一股NTR的味道···?)

何为双token验证

​ 顾名思义,就是在登陆操作之后由服务端返回两个token:accessToken和refreshToken,在之后的验证登录态的操作中使用这两个token进行验证,其中accessToken的过期时间相当短,refreshToken的过期时间相对于accessToken而言相当长,且会不断的刷新,每次刷新后的refreshToken都是不同的

双token验证的优点

通过上面的描述也可以或多或少的看出双token验证机制的优点了:

  • accessToken的存在,保证了登录态的正常验证,因其过期时间的短暂也保证了帐号的安全
  • refreshToekn的存在,保证了用户(即使是非活跃用户)无需在短时间内进行反复的登陆操作来保证登录态的有效性,同时也保证了活跃用户的登录态可以一直存续而不需要进行重新登录,其反复刷新也防止某些不怀好意的人获取refreshToken后对用户帐号进行动手动脚的操作(拒绝NTR.jpg)

双token检验流程

​ 首先进行正常的登录操作,在后台服务器验证账号密码成功之后返回2个token:accessToken和refreshToken。在进行服务器请求的时候,先将Token发送验证,如果accessToken有效,则正常返回请求结果;如果accessToken无效,则验证refreshToken。此时如果refreshToken有效则返回请求结果和新的accessToken和新的refreshToken。如果refreshToken无效,则提示用户进行重新登陆操作。

​ 流程图如下:

双token检验流程图

token的时间设置

​ token的时间设置需要看需求进行划分区别设置:

PC网络应用

​ 对于网络应用程序而言,由于token可以直接直观地获取到,因此不管是accessToken还是refreshToken为了安全起见,其过期时间都不应该设置得很长,且需要不停地更换token,因此PC网络应用的accessToken一般设置为2h过期,而refreshToken设置为1天到2天比较好,不足1天也是可以的,如果设置的时间比较短就在活跃期间时常刷新freshToken就好了,如果设置的时间比较长,就只需要设置一个阈值(比如7day的refreshToken设置一个6day阈值),在refreshToken小于等于这个阈值的时候就进行刷新refreshToken就好了。

手机应用

​ 对于手机APP应用而言,登录操作一般只做一次,因此token的过期时间必是无限,即不会过期,不过为了安全起见(比如防止你丢手机),token应该以某种程度上对用户可见(比如在安全中心里检验了身份之后可以让你看到哪些设备有token,即哪些设备会被允许登录)并可让用户对其进行一定程度上的操作(比如你手机丢了,然后登录安全中心移除那个手机的token,也就是移除那个手机的登陆权限,从而使那个手机的应用上的你的帐号强制下线)

无效的Token的处理

对于频繁更换的Token,如何处理旧的未过期的而又无效的refreshToken,以下提供了几个思路:

1) 简单地从浏览器中移除token就好了

显然,这种方式对于服务器方面的安全而言并没有什么卵用,但它能通过移除存在的token来阻止攻击者(比如,攻击者必须在用户下线之前窃取到token)

2) 制作一张token黑名单

在移除了浏览器存储的token后如果还想要再严格点,就只能在服务器上制作一张已经无效但是没过期的token的黑/白名单了,在每次请求中都操作数据库进行token的匹配,并以某种方式进行维护(不管是黑名单的定期删除维护也好,白名单的无效时删除也好),不过显然这种方式还是违背了token无状态的初衷,但是除此之外也没别的办法。

​ 存储可以按照userId—token的方式存储在数据库中(当然也可以按你喜欢添加其他字段标明其他信息,比如说mac地址啦,是手机还是电脑啦,设备型号啦,巴拉巴拉巴拉····),白名单的话直接存储有效的token,在需要token无效的逻辑中删除指定token即可(比如刷新token的时候把旧的无效的但未过期的删掉)。而如果是黑名单的话就需要你定期去删除其中已经过期的token了。

​ 而验证的话除了要去数据库名单里匹配之外还需要验证token本身的有效性。

3)只需要将token的过期时间设置的足够短就行了

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐