知微坚果的拾光小镇 知微坚果的拾光小镇
首页
随笔
  • Golang

    • 基础
    • 第三方库
  • 前端

    • Vue
    • Flutter
  • 开发工具
  • 系统工具
  • Timer (opens new window)
  • 时间线
  • 关于
GitHub (opens new window)

知微坚果

行者常至,为者常成
首页
随笔
  • Golang

    • 基础
    • 第三方库
  • 前端

    • Vue
    • Flutter
  • 开发工具
  • 系统工具
  • Timer (opens new window)
  • 时间线
  • 关于
GitHub (opens new window)
  • JWT服务端主动失效方案

  • 后端
  • 方案
知微坚果
2020-09-11
目录

JWT服务端主动失效方案

引言: 使用JWT时,有一个十分头疼问题就是:用户主动注销、强制登出(禁止登陆)、忘记密码、修改密码、JWT续签、踢出下线时,服务器不能让token主动失效!

本文将探索关于这个问题的解决方案。

# 目录:

[toc]

  1. 什么是JWT
  2. JWT的特点
  3. JWT主动失效方案
  4. JWT主动失效最佳实践
  5. 进阶优化
  6. 总结

# 1.什么是JWT

JWT(JSON Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

# 2.JWT的特点

  1. 无状态: JWT包含认证信息,token只需要保存在客户端,服务端不需要保存会话信息,所以JWT是无状态的。 这一点使得服务器压力大大降低,增加了系统的可用性和可扩展性。 但是无状态同时也是JWT最大的缺点,服务器无法主动让token失效。

  2. 安全性:客户端每次请求都会携带token,可以有效避免CSRF攻击,同时token会自动过期,可以减少token被盗用的情况,服务器会通过jwt签名验证token,可以避免token被篡改。

  3. 可见性: JWT的payload部分可以直接通过Base64解码,所以可存储一些其他业务逻辑所必要的非敏感信息。

# 3.JWT主动失效方案

由JWT的特点可知,JWT是无状态的,并且JWT的过期时间是签发的时候确定的,无法动态修改, 所以服务端如何让JWT主动失效成为了一个问题。 下面是几种让JWT主动失效的方案

  1. 服务器生成token时,将每一个token都保存在Redis中,每次请求都会直接对比用户携带的token和redis中的token,如果token相同,则通过验证,否则判定为token失效。
  2. 版本号校验:token和redis中保存用户的token版本号,每次请求对比版本号,一致就通过,不一致就拒绝,主动失效时直接使redis中该用户token版本号+1.
  3. 服务端保存一个token黑名单,服务器将需要失效的token放入黑名单,每次请求判断携带的token是否在黑名单之中。
  4. redis保存主动过期时间,每次请求获取token的签发时间,和redis中的过期时间对比,小于过期时间则失效。
  5. 签名校验,数据库中每个用户保存一个token签名,签发token时携带签名信息,redis中保存过期签名信息黑名单。每次比对签名,在黑名单,则失效。

# 4.JWT主动失效最佳实践

通过了解上面的JWT主动失效方案,下面结合实际业务需求, 总结一种可以应用于生产的最佳实践方案:

  1. 首先保证token过期时间不要太长,一般120分钟比较合适。
  2. token必须携带签发时间
  3. 通过redis保存一个黑名单,key包含用户id,value为主动失效时间戳。
  4. 每次请求,通过token基本校验之后,查询黑名单中是否有该用户id,如果有,那么比较token签发时间和黑名单中的主动失效时间,如果签发时间早于失效时间,那么表示token失效,拒绝请求,否则允许访问。

# 关于黑名单的实现:

可以直接使用redis保存key-value,key中携带用户id,value为失效时间,这样的话就可以直接使用redis的自动过期,这个过期时间设置为token有效时长(如前面的120分钟)

# 实现主动失效:

当需要主动让某用户token失效时, 可以组合当前用户id和当前时间戳保存在redis中,并设置有效时长。

# 5.进阶

对于上面这种方案, 每次让用户token失效,则会使得该用户在失效前签发的所有token都失效,无法更加精确的控制token的失效性。

# 优化1:

如果实际需求是,有多个应用产品,比如Android、IOS、Windows和Web多端产品,业务要求可以每次控制失效的具体是哪个产品申请的token。

这种情况,我们可以在JWT的payload中保存一个请求签发的token的具体客户端应用标识,比如(android), 而redis过期黑名单中的key值,除了携带用户id,再加上具体想失效的应用标识即可。

当然,如果要具体到每一个token,只能采用上面的方案1了。

# 优化2:

如果实际应用对访问速度和性能要求比较高,用户每次请求都要连接redis读取信息,是比较浪费的。 实际上,redis作为内存数据库,其速度已经相当的快了,不过实际场景可能会是分布式缓存系统,redis连接和读取还是会有一定的ttl延迟。 优化办法就是:需要做JWT校验的服务器应用,在启动时,访问分布式缓存中的token过期黑名单,将其保存在应用的本地内存当中。同时订阅分布式缓存的消息推送,在黑名单信息发生变化是,进行数据同步。

# 6.总结

通过上面的解决方案实践和优化 我们就可以实现服务器端主动让token失效的功能了。 而在具体的应用场景有:

用户主动注销、强制登出(禁止登陆)、忘记密码、修改密码、JWT续签、踢出下线等

实现的代价就是需要使用redis缓存黑名单, 在一定程度上破坏了JWT无状态的特性, 但是实际上需要主动使JWT失效的情况只占整个活动用户的很小一部分, 所以相比较于分布式Session,JWT实现的方式需要的存储空间很小。

对于进阶优化2,使用了分布式缓存和应用本地同步,增加了方案的复杂性, 不过对于大部分的应用场景来说,都具有完整的分布式缓存、消息分布订阅组件,只需要直接使用现成的即可,花费的代价并不多。

究竟采用哪种方案实现,都应该结合具体的业务需求的,并不存在真正完美的方案。

#后端
更新时间: 12/2/2022, 12:38:29 AM
最近更新
01
SpringBoot3.0快速上手
12-07
02
Spring事务管理源码分析
12-06
03
Golang工程结构最佳实践
12-04
更多文章>
Theme by Vdoing | Copyright © 2022-2022 知微坚果 | 拾光小镇
蜀ICP备17001150号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式