JWT 登录态怎么设计才不踩坑:Access Token 过期与 Refresh Token 轮换实战
很多登录系统在开发环境跑得很好,一上线就陆续出问题:用户正在填表单突然被踢出登录,刷新页面后又莫名其妙恢复;换了密码,旧设备还能继续访问;更麻烦的是,被盗用的 Refresh Token 可以反复换取新的 Access Token,等于登录态永远不过期。本文把 Access Token 过期处理和 Refresh Token 轮换讲清楚,给出一套可以直接落地的设计。
一、先把两个 Token 的分工定死
Access Token 只负责证明“这次请求是谁”,有效期建议 15 分钟左右,里面只放用户标识、角色和 token 唯一编号 jti,不要放手机号、权限明细等敏感信息,更不要把它当成数据库。Refresh Token 只负责换取新的 Access Token,有效期可以是 7 到 14 天,但它本身不能直接调用业务接口。分工不清是很多坑的根源:把 Refresh Token 当 Access Token 用,或者把 Access Token 有效期设成 30 天,都会让一次泄露的代价成倍放大。
二、Access Token 过期不要直接跳登录
前端收到 401 时,先不要立刻清除登录态并跳转。正确流程是:拦截器发现 Access Token 过期,暂停后续请求并只发起一次刷新;刷新成功后,用新的 Access Token 重放被挂起的请求;刷新失败再清除登录态并引导重新登录。这里有三个细节:同一时间多个请求触发 401 时必须合并为一次刷新,避免并发刷新把旧 Refresh Token 误判为重放;刷新请求本身不要走同一个拦截器,否则容易死循环;刷新接口失败要区分是 Refresh Token 过期还是被吊销,前者可以提示重新登录,后者要提示账号可能存在风险。
三、Refresh Token 轮换是核心防线
每次刷新都必须同时签发新的 Refresh Token,并立即作废旧的。服务端不要只存明文 Token,而应存哈希值、所属设备、家族编号和过期时间。同一家族内出现旧 Token 被再次使用的情况,说明 Token 可能已被复制,这时要作废整个家族的 Refresh Token,强制所有设备重新登录,并给用户发送安全提醒。轮换还要配合重放窗口做容错:网络重试可能导致客户端拿到新 Token 却没保存成功,短暂的宽限期可以避免正常用户被误杀,但宽限期必须很短,且只能用于最近一次轮换。
四、存储、退出与改密码要一起设计
Access Token 可以放在内存中,随页面刷新丢失也没有关系,因为 Refresh Token 会负责续期。Refresh Token 必须走 HttpOnly、Secure 的 Cookie,设置 SameSite,并把刷新接口路径限定在刷新专用路径,避免每个请求都携带它。绝对不要把 Refresh Token 放进 localStorage,否则一次 XSS 就等于长期登录凭证被盗。退出登录时,后端要删除对应的 Refresh Token 记录;修改密码或发现异常时,要吊销该用户全部家族的 Refresh Token,并让 Access Token 里的密码版本号失效,这样旧设备即使拿着未过期的 Access Token,也会在最长 15 分钟内失去权限。
五、上线前自查清单
第一,Access Token 是否短有效期且不含敏感字段;第二,刷新是否单例化、有无死循环;第三,轮换后旧 Token 是否真正作废,重放是否会触发家族吊销;第四,Cookie 属性和刷新路径是否收紧;第五,退出、改密、封禁是否都能让登录态失效。把这五项跑通,登录态就不再是“能用就行”,而是过期可控、泄露可止损的设计。
- 点赞
- 收藏
- 关注作者
评论(0)