没有什么比在用户操作得正嗨时,突然提示“登录已过期,请重新登录”的提示更让人沮丧的了。这种突兀的中断不仅破坏了用户体验,甚至可能导致未保存的数据丢失。
然而,我们都知道,出于安全考虑,用于身份验证的 Token(通常是 Access Token)必须有较短的有效期。那么,我们如何在保证安全的前提下,创造一种“永不掉线”的丝滑体验呢?
问题的根源:Access Token 的“天生矛盾”
首先,我们要理解为什么需要刷新 Token。
我们通常使用 Access Token 来验证用户的每一次 API 请求。为了安全,Access Token 的生命周期被设计得很短(例如 30 分钟或 1 小时)。如果有效期太长,一旦泄露,攻击者就能在很长一段时间内冒充用户进行操作,风险极高。
这就产生了一个矛盾:
- 安全性要求:
Access Token有效期要短。 - 用户体验要求:用户不想频繁地被强制重新登录。
为了解决这个矛盾,Refresh Token 应运而生。
核心理念:双 Token 认证系统
无感刷新机制的核心在于引入了两种类型的 Token:
Access Token(访问令牌)- 用途:用于访问受保护的 API 资源,附加在每个请求的
Header中。 - 特点:生命周期短(如 1 小时),无状态,服务器无需存储。
- 存储:通常存储在客户端内存中(如 Vuex/Redux),因为需要频繁读取。
Refresh Token(刷新令牌)- 用途:当
Access Token过期时,专门用于获取一个新的Access Token。 - 特点:生命周期长(如 7 天或 30 天),与特定用户绑定,服务器需要安全存储其有效性记录。
- 存储:必须安全存储。最佳实践是存储在
HttpOnlyCookie 中,这样可以防止客户端 JavaScript 脚本(如 XSS 攻击)读取它。
既然如此,为何不直接使用 Refresh Token 呢?
Access Token 通常是无状态的,服务器无需记录它,也导致 JWT 无法主动吊销,而 Refresh Token 是有状态的,服务器需要一个列表(数据库中的“白名单”或“吊销列表”)来记录哪些 Refresh Token 是有效的,当用户更改密码、或从某个设备上“主动登出”时,服务器端可以主动将对应的 Refresh Token 设为无效。
无感刷新的详细工作流
下面是这个“魔法”发生的具体步骤:
- 首次登录:用户使用用户名和密码登录。服务器验证成功后,返回一个
Access Token和一个Refresh Token。 - 正常请求:客户端将
Access Token存储起来,并在后续的每次 API 请求中,通过Authorization请求头将其发送给服务器。 - Token 过期:当
Access Token过期后,客户端再次用它请求 API。服务器会拒绝该请求,并返回一个特定的状态码,通常是401 Unauthorized。 - 拦截 401 错误:客户端的请求层(如 Axios 拦截器)会捕获这个
401错误。此时,它不会立即通知用户“你已掉线”,而是暂停这个失败的请求。 - 发起刷新请求:拦截器使用
Refresh Token去调用一个专门的刷新接口(例如/api/auth/refresh)。 - 处理刷新结果:
- 刷新成功:服务器验证
Refresh Token有效,生成一个新的Access Token(有时也会返回一个新的Refresh Token,这被称为“刷新令牌旋转”策略,可以提高安全性),并将其返回给客户端。 - 刷新失败:如果
Refresh Token也过期了或无效,服务器会返回错误(如403 Forbidden)。这意味着用户的登录会话彻底结束。 - 重试与终结:
- 若刷新成功:客户端用新的
Access Token自动重发刚才失败的那个 API 请求。用户完全感觉不到任何中断,数据操作无缝衔接。 - 若刷新失败:客户端清除所有认证信息,强制用户登出,并重定向到登录页面。
实战演练:使用 Axios 拦截器实现无感刷新
Axios 的拦截器是实现这一流程的完美工具。下面是一个完整且考虑了并发问题的实现方案。
1. 创建 Axios 实例
首先,我们创建一个单独的 Axios 实例,方便统一管理。
2. 核心:响应拦截器
这是实现无感刷新的关键。
代码解析:
- 并发处理:
isRefreshing标志和requests数组是关键。当第一个401错误触发刷新时,isRefreshing变为true。后续在刷新完成前到达的401请求,都会被推进requests队列中挂起,而不是重复发起刷新请求。当刷新成功后,再遍历队列,依次执行这些被挂起的请求。 - 原子操作:通过这种“加锁”机制,确保了刷新 Token 的操作是原子的,避免了资源浪费和潜在的竞态条件。
- 优雅降级:当
Refresh Token也失效时,系统会执行logoutUser(),进行清理工作并引导用户重新登录,这是一个优雅的失败处理方案。
无感刷新 Token 机制是现代 Web 应用提升用户体验的“标配”。它将身份验证的复杂性隐藏在后台,为用户提供了一个流畅、不间断的操作环境。
实现这一机制,不仅仅是写几行代码,更是对认证流程、安全性和用户体验三者之间平衡的深刻理解。
上一篇文章