为什么需在服务端数据库存储JWT的refresh token?存储access token是否更好?
JWT会话方案相关问题解答
为什么需要存储refresh token
即使refresh token是服务端签名、不可篡改、可自主校验过期,存储的核心作用是实现主动令牌作废能力,解决两个核心问题:
- 令牌泄露后可以立即作废被盗的refresh token,避免攻击者在令牌有效期内持续申请新的access token窃取用户信息
- 支持多端登录管理,用户可以单独登出某一台设备的会话,不会影响其他设备的正常登录
如果不存储refresh token,服务端只能被动接受所有签名有效、未过期的refresh token,完全没有主动管控会话的能力。
能否只存储user_id替代存储refresh token
不能,会出现两个明显缺陷:
- 无法区分同个用户的多端会话,只要是该用户的有效refresh token都会被认可,用户登出时只能作废该用户所有会话,无法单独指定某一端登出,体验极差
- 无法限制refresh token的使用次数,正常方案中refresh token可以设计为单次使用(每次刷新就生成新的refresh token同时作废旧的),如果只存user_id完全做不到这个逻辑,泄露的refresh token可以一直用到过期。
存储最新access token的方案是否有优势
该方案的唯一收益是可以解决你提到的「旧access token未过期时登出仍可使用」的问题,但弊远大于利:
- 完全丧失JWT的无状态优势,每次业务接口请求都需要查询Redis校验access token是否为最新值,Redis的查询压力会比存储refresh token的方案大几十上百倍:毕竟refresh token只有在access token过期时才会调用,频率远低于普通业务接口
- 你预设的「只会在前一个access token过期后才生成新的access token」逻辑在实际场景中很难落地:客户端时间误差、网络波动、用户切网等场景都可能触发提前刷新请求,如果严格限制只有过期才能刷新,会频繁出现用户正常使用时被迫重新登录的问题
- 该方案本质已经退化为类Session的实现,和直接用Session ID存用户状态没有本质区别,完全没必要额外引入JWT的复杂度
针对你的应用场景的优化建议
- 优先级最高的操作是开通HTTPS,你当前未使用HTTPS的情况下,所有httpOnly cookie、令牌签名的防护都完全无效,中间人可以直接抓包获取所有令牌,没有任何会话安全可言
- 如果要降低Redis存储成本,可以只存refresh token的哈希值而非明文,验证时将客户端提交的refresh token做哈希后对比即可,安全性更高
- 可以给每个refresh token增加唯一标识
jti字段,Redis中以user_id:jti为键存储对应令牌的过期时间,登出时只需要删除对应jti的键即可实现单端登出,灵活性更高
内容的提问来源于stack exchange,提问作者Karthik Prakash
相关产品推荐
相关产品推荐

