先记住这个答案
双令牌机制让短生命周期的 Access Token 承担资源访问,长生命周期的 Refresh Token 专门用于换发新令牌。设计上 Access Token 通常放在内存或 HttpOnly Cookie 中,有效期几分钟到一小时;Refresh Token 必须存放在更安全的位置如 HttpOnly Cookie,并做轮换与重放检测。传输上 Access Token 每次请求携带,Refresh Token 只在后端换取时出现。这样即使 Access Token 泄露,攻击者也只能在短期内滥用,且可通过撤销 Refresh Token 阻断后续换发。
- Access Token 短命减少泄露危害
- Refresh Token 需更安全存储与轮换
- 刷新令牌丢失需及时撤销
双令牌的分工与刷新机制
Access Token 是资源服务器信任的凭证,应用每次请求都要携带以访问受保护资源。如果它的有效期过长,一旦泄露,攻击者就能长时间冒充用户;有效期过短,用户频繁注销重登体验极差。Refresh Token 因此出现:它专门用于换发新 Access Token,以延长会话而无须反复出示主凭证。
刷新流程以 OAuth 2.0 为例:Access Token 过期后,客户端将 Refresh Token 发送到授权服务器的刷新端点,授权服务器先验证 Refresh Token 的有效性与作用域,再签发新的 Access Token 和可选的 Refresh Token。活跃用户几乎无感知地续用,而每次 Request 中的 Access Token 始终短命,从而压缩泄露窗口。
移动端长会话与泄露控制
假设一个移动办公应用,Access Token 设置为 30 分钟有效,Refresh Token 设置为 7 天。用户每半小时内切换界面触发请求,若收到 401,SDK 在后台用 Refresh Token 请求新令牌,用户无感。但若恶意软件在设备上窃取了 Refresh Token,它可在 7 天内不断换取新令牌,伪造长期合法身份。
对策是强制 Refresh Token 轮换:每次刷新时,服务端签发一个新 Refresh Token 并作废旧 Refresh Token。若攻击者重放旧 Refresh Token,新 Refresh Token 已被标记为已使用,服务端应拒绝并撤销整条会话链,同时向用户告警。移动端还需把 Refresh Token 存进系统安全存储(如 Keychain),而非普通文件或偏好。
不能只靠 Refresh Token 的场景
在单页应用(SPA)中,若 Refresh Token 存于 localStorage,XSS 可直接读取;即便放入 HttpOnly Cookie,仍需处理 CSRF。此外,Refresh Token 需要服务端状态化才能支持撤销,否则 JWT 本身无法主动失效。一旦 Refresh Token 被泄露且未采用轮换检测,攻击者就能持续换发新令牌,此时只能通过强行撤销全部会话与异常检测兜底。
设计时必须明确:Refresh Token 的保密级别高于 Access Token,它不能出现在 URL、日志或第三方脚本可见的存储中。若业务要求更高的安全性,可将 Access Token 的寿命缩短到分钟级,但刷新频率上升,需平衡密钥管理的复杂度与可用性。实际系统可在刷新端加上频率限制与设备指纹,降低批量泄露风险。
容易答错的地方
- Refresh Token 比 Access Token 更安全,可以随意放在前端
- 纠正:Refresh Token 有效期长,一旦泄露,攻击者可长期换取新 Access Token,风险远大于一次性使用的 Access Token。因此它必须放入 HttpOnly Cookie 或安全存储,并配合轮换与重放检测,而不是随手放在 localStorage。
- Access Token 短命,所以 Refresh Token 可以长期不换
- 纠正:Refresh Token 即使有轮换,也不能无限期使用。通常应设置绝对过期时间(如 30 天),并在用户在活跃时持续续期。若 Refresh Token 永远不换,旧令牌一旦泄露就破坏整个会话安全。做法每次刷新时都生成全新的 Refresh Token 并作废旧值。
面试官还会怎么问?
Refresh Token 轮换时如果出现并发刷新,会怎样处理?
并发刷新会导致旧 Refresh Token 被同时使用,服务端应支持版本号或原子更新来区分。当检测到旧令牌被重放时,应撤销整个会话并要求重新认证,而不是简单签发新令牌。
Refresh Token 与 Session ID 在实现上有什么本质区别?
Refresh Token 是一种长期凭证,可以带 scope 限制且用于换短令牌;Session ID 直接绑定服务端会话,但两者都需要服务端存储来支持主动注销。Refresh Token 更适合跨域与 API 场景,Session 更紧密地与单站点会话状态耦合。
如何存储 Refresh Token 才能平衡 XSS 与 CSRF 风险?
在浏览器中,建议放在 HttpOnly + Secure + SameSite=Strict 的 Cookie 中以防止 XSS读取,同时需要额外 CSRF Token 保护刷新端点;在 App 中则放系统安全存储。若放 localStorage 则必须彻底消除 XSS。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。