双Token的意义以及RefreshToken的安全疑虑

68 阅读6分钟

认证系统中双token有什么意义

在认证系统中,双 Token 机制(通常指 Access Token 和 Refresh Token)的出现,主要是为了平衡安全性(Security)用户体验(User Experience) 之间的矛盾。

如果我们只使用一个永久有效的 Token,一旦泄露,黑客将永久拥有访问权限;如果我们使用一个有效期极短的 Token,用户就不得不频繁重新登录,体验极差。

以下是双 Token 机制的核心意义和工作流程:


1. 核心组件及其角色

组件名称有效期存储位置作用
Access Token访问令牌短(如 15 分钟)内存或客户端状态用于请求受保护的资源(API 调用)。
Refresh Token刷新令牌长(如 7 天或更久)HttpOnly Cookie 或数据库专门用于在 Access Token 过期后申请新的 Access Token。

2. 双 Token 的核心意义

A. 降低泄露风险(缩短攻击窗口)

Access Token 会在每次 API 请求中通过 HTTP 头部传输,拦截风险较高。由于它的有效期很短,即便黑客窃取了该 Token,其利用价值也很快会失效。

B. 实现“无感刷新”用户体验

当 Access Token 过期时,客户端可以自动在后台使用 Refresh Token 换取新的 Access Token。用户不需要重新输入账号密码,整个过程对用户是透明的。

C. 权限的即时回收(Revocation)

这是最关键的一点。Access Token 通常是无状态的(如 JWT),服务端很难在中途撤回。

  • 通过双 Token,服务端可以将 Refresh Token 存储在数据库中。
  • 如果发现用户异常或用户主动登出,服务端只需注销该 Refresh Token
  • 下一次 Access Token 过期时,由于 Refresh Token 已失效,用户将无法再获取新的访问权限,从而强制下线。

3. 工作流程图解

  1. 登录成功:服务器返回 Access Token (AT) 和 Refresh Token (RT)。
  2. 正常访问:客户端携带 AT 访问资源,服务器校验成功,返回数据。
  3. AT 过期:客户端携带 AT 访问,服务器返回 401 Unauthorized(Token 已过期)。
  4. 刷新环节:客户端截获 401 错误,自动将 RT 发送给授权服务器。
  5. 更新成功:服务器校验 RT 有效,下发新的 AT(有时也会更换 RT)。
  6. 重试请求:客户端拿到新 AT 后,重新发起之前失败的请求。

4. 进阶安全:Refresh Token Rotation(刷新令牌滚动)

为了进一步增强安全性,现代系统常采用 RT 滚动机制

  • 每当使用一次 Refresh Token 时,服务器都会将其作废,并颁发一个新的 Refresh Token。
  • 意义:如果黑客窃取了 RT 并先于用户刷新,当真实用户尝试刷新时,服务器会发现该 RT 已被使用过,从而意识到发生了安全威胁,可以立即冻结该用户的所有会话。

双Token的意义总结

双 Token 机制通过 “短寿命的访问权” 配合 “长寿命的更新权”,解决了分布式系统下状态同步的难题,既保障了敏感操作的安全性,又避免了用户被频繁踢下线的烦恼。

如果您想深入了解如何在具体的开发框架(如 Spring Security 或 Node.js)中实现这一套流程,我可以为您提供相关的代码示例。

我有个疑问,refreshToken泄露那不是可以直接拿到新的token,客户端可以自动在后台使用 Refresh Token 换取新的 Access Token。那对于黑客不是一样可以,无限续杯了?

你的直觉非常敏锐!这确实是双 Token 机制中最核心的攻击面:如果 Refresh Token (RT) 也丢了,该怎么办?

确实,如果黑客拿到了 RT,他们确实可以实现“无限续杯”。但双 Token 机制之所以被称为“更安全”,是因为它为这种情况准备了多重防御深度(Defense in Depth)


1. 暴露面的差异:RT 比 AT 更难被偷

  • Access Token (AT) :每一分钟、每一次请求都在网络上跑,暴露在 Header 中,被拦截(中间人攻击、日志泄露)的概率极高。

  • Refresh Token (RT)

    • 传输频率低:只有在 AT 过期时(如每小时一次)才传输一次。
    • 存储更安全:在 Web 端,RT 通常存储在 HttpOnly Cookie 中,这意味着 JavaScript 脚本(XSS 攻击)无法读取它,而 AT 为了方便调用 API,往往存留在 LocalStorage 或内存中,容易被 XSS 攻击窃取。

2. 核心大招:Refresh Token Rotation(RT 旋转/滚动)

为了应对你说的“无限续杯”,现代认证协议(如 OAuth 2.1)引入了 RT 旋转机制

  • 一次性策略:每当 RT 被用来换取新的 AT 时,旧的 RT 会立即失效,服务器会返回一个新的 RT。

  • 冲突监测(关键点) :如果黑客偷走了 RT 并抢先刷新了 Token,那么当真正的用户(合法客户端)尝试刷新时,它会使用那个已经失效的旧 RT

  • 一锅端处理:一旦服务器收到一个“已使用过”的 RT 请求,它会判定发生了安全入侵。此时服务器会使该用户下所有关联的 Token 全部失效

    • 结果:黑客虽然续杯了一次,但很快就会和真用户一起被踢下线,强制重新登录。

3. 风险可控:服务端拥有“撤回权”

在单 Token 模式(如纯 JWT)中,Token 一旦发出去,就像泼出去的水,服务器在有效期内无法撤回(除非维护一个庞大的黑名单)。

但在双 Token 模式下:

  • AT 是无状态的:快速过期,不需要查库,性能高。
  • RT 是有状态的:服务器通常会在数据库或 Redis 中记录 RT 的状态。
  • 手动封禁:如果后台监测到某个账号异常(如异地登录、高频操作),管理员可以直接在数据库删除对应的 RT。黑客手中的 AT 最多只能再跳腾几分钟,随后便会因为无法刷新而彻底失效。

4. 总结对比

场景单 Token (长效)双 Token (AT 短 + RT 长)
泄露后果永久控制权,除非修改密码或等过期。只有分钟级的控制权。
检测手段无法检测。通过 RT 旋转机制发现并终止攻击。
封禁能力很难,需要黑名单,开销大。很容易,直接在后端作废 RT 即可。

简单一句话:

RT 泄露确实危险,但通过 HttpOnly 存储、降低传输频率、一次性滚动机制 以及 后端可撤回性,它比“一个 Token 走天下”要安全得多。

你想看看在前端代码中如何优雅地处理“RT 换 AT”并自动重试失败请求的逻辑吗?