JWT 鉴权中的单 Token 与双 Token 方案对比:控制力、安全性与实用性解析

在使用 JWT 进行身份验证与续签时,常见有两种方案:

  • 方案一:双 Token 模式(Access Token + Refresh Token)
  • 方案二:单 Token 模式(JWT 中嵌入续签时间)

这两种方案都在社区中被使用,但在安全性、控制力和设计合理性上存在关键差异。本文将系统性地比较这两种方案,并解释为什么“双 Token 模式”在实际项目中被广泛采用。


✅ 背景介绍

方案一:双 Token 模式

  • access_token:短期有效,用于访问受保护资源,通常 15-30 分钟过期;
  • refresh_token:长期有效,用于换发新的 access_token,通常存放在 HttpOnly Cookie,1 小时~7 天过期。

续签过程必须通过服务器验证 refresh_token,服务器主动决定是否签发新 token。

方案二:单 Token 模式

  • 仅使用一个 JWT;
  • JWT 中添加一个自定义字段,如 renew_after
  • 当 token 尚未过期但已超过 renew_after 时间,客户端自动请求续签;
  • 服务器根据 token 的状态和其他逻辑决定是否签发新 token。

🎯 本质区别:控制点与安全边界

✅ 方案一的设计优势:清晰分权,服务端可控

  • 续签逻辑必须走服务器,服务器拥有完全的决策权
  • refresh_token 可以存储上下文信息,如 IP、User-Agent、设备 ID;
  • 可以配合数据库或 Redis 实现状态管理、吊销机制;
  • 清晰分离两个 token 的职责 —— 一个访问资源,一个控制续签。

⚠️ 方案二的问题:逻辑耦合,服务端被动判断

  • 一个 token 同时承担访问和续签判断,职责不清晰;
  • 续签行为依赖客户端逻辑,服务端只能被动“拒绝续签”;
  • 若要实现撤销,必须维护黑名单系统,违背 JWT 无状态设计;
  • 安全边界模糊,难以处理设备隔离、上下文绑定等需求。

🛡️ 安全性比较

比较维度 双 Token 模式 单 Token 模式
token 泄露后影响范围 access_token 泄露影响小(短期) 唯一 token 泄露即拥有访问 + 续签能力
refresh_token 是否 HttpOnly 存储 ✅ 是 ❌ 无(或不存在 refresh_token)
是否支持服务端立即吊销续签能力 ✅ 可以(拒绝 refresh_token) ⚠️ 只能靠黑名单绕过 JWT 无状态限制
是否能绑定设备/上下文信息 ✅ 可绑定并检查 ⚠️ 难以实现,需自行扩展校验逻辑

🏗️ 架构与扩展性对比

比较维度 双 Token 模式 单 Token 模式
OAuth2 等标准支持 ✅ 完全兼容 ❌ 非标准自定义逻辑
与移动端或 SPA 接入兼容性 ✅ 好(HttpOnly Cookie 配合可选) ⚠️ 移动端需扩展 token 管理
控制粒度 ✅ 高(每个 token 可单独管理) ⚠️ 低(token 自包含,服务端只能被动处理)
设计职责清晰度 ✅ 分离访问与续签 ❌ 访问与续签混杂在一个 token 内
token 续签逻辑可追踪性 ✅ 强(refresh_token 可追踪) ⚠️ 弱(单 token 无状态不可追踪)

👨‍💻 那单 Token 模式真的不行吗?

不是。

单 token 模式 理论上可以实现大多数控制逻辑,包括:

  • 黑名单;
  • Token 数据库存储与状态管理;
  • 上下文校验(IP、设备等);
  • 主动拒绝续签请求。

但这些实现通常是对 JWT 无状态设计的“补丁”:

  • 自定义字段;
  • 额外存储;
  • 黑名单机制;
  • 逻辑嵌套在 JWT 校验中。

因此:

如果你愿意承担额外的实现和维护成本,单 token 模式也是可行的,但它违背了 JWT 的无状态特性,也难以在大型系统中长期维护。


🔚 总结

项目 双 Token 模式 单 Token 模式
控制力 ⭐⭐⭐⭐⭐ ⭐⭐
实现复杂度 ⭐⭐⭐
安全性 ⭐⭐⭐⭐ ⭐⭐
标准兼容性 ⭐⭐⭐⭐⭐ ⭐⭐
实际应用广度 广泛应用(主流) 偶有使用(非主流)

推荐:生产环境优先采用双 token 模式,将访问控制与续签控制职责分离,提升安全性与可控性。


📎 附录:什么是 HttpOnly Cookie?

  • HttpOnly Cookie 是浏览器中只能被服务器访问的 cookie,JavaScript 无法读取;
  • 常用于存储 refresh_token,有效防止 XSS 攻击;
  • 设置方式:
    • Java
      1
      2
      3
      4
      5
      6
      7
      8
      public void setHttpOnlyCookie(HttpServletResponse response, String name, String value) {
      Cookie cookie = new Cookie(name, value);
      cookie.setHttpOnly(true); // 防止 JavaScript 访问
      cookie.setSecure(true); // 仅 HTTPS 下传输(生产环境推荐开启)
      cookie.setPath("/"); // 有效路径
      cookie.setMaxAge(60 * 60 * 24); // 有效期(秒)
      response.addCookie(cookie);
      }
    • Node/Express: res.cookie('refresh_token', token, { httpOnly: true, secure: true })
    • Flask: resp.set_cookie('refresh_token', token, httponly=True)

如果你正在设计一个带身份认证的前后端分离系统,建议选择双 token 模式,并结合 HttpOnly Cookie 存储 refresh_token,以获得更高的安全性与控制能力。

最后附上,个人博客网站:Southblock’Blog,内容更多,更新,欢迎参观。