编程 无感刷新 Token:打造“永不掉线”的丝滑用户体验

2025-08-15 15:23:36 +0800 CST views 17

无感刷新 Token:打造“永不掉线”的丝滑用户体验

没有什么比在用户操作正嗨时,突然弹出“登录已过期,请重新登录”的提示更让人沮丧了。这种突如其来的中断不仅破坏了用户体验,还可能导致未保存的数据丢失。

然而,为了安全考虑,用于身份验证的 Access Token 必须设计成短生命周期。这就产生了一个矛盾:

  • 安全性要求:Token 生命周期短,防止泄露带来长时间风险
  • 用户体验要求:用户不希望频繁重新登录

解决这个矛盾的关键是 无感刷新机制(Silent Refresh Token),通过 Access Token + Refresh Token 的双 Token 体系,实现用户几乎感受不到的登录刷新体验。


1. 为什么需要 Refresh Token

在现代 Web 应用中,我们通常使用 JWT 或 Access Token 来验证每一次 API 请求。

Access Token 生命周期短(如 30 分钟到 1 小时),一旦泄露,攻击者可能利用它执行敏感操作。

问题:如果直接延长 Access Token 的有效期,安全性降低;如果保持短有效期,用户频繁掉线。

解决方案是引入 Refresh Token

Token 类型用途特点存储位置
Access Token访问受保护资源生命周期短、无状态内存 / Vuex / Redux
Refresh Token获取新的 Access Token生命周期长、有状态、可吊销HttpOnly Cookie(防 XSS)

注意:Access Token 通常无状态,服务器无法主动吊销;Refresh Token 是有状态的,服务器可以在用户登出或修改密码时主动作废。


2. 无感刷新工作流程

完整流程如下:

  1. 首次登录
    用户使用用户名和密码登录,服务器返回 Access Token + Refresh Token。

  2. 正常请求
    客户端将 Access Token 附加在请求头中访问 API。

  3. Access Token 过期
    API 返回 401 Unauthorized

  4. 捕获 401 并触发刷新
    客户端拦截器捕获 401 错误,并暂停失败请求。

  5. 刷新 Access Token
    使用 Refresh Token 调用 /api/auth/refresh 接口:

    • 成功:返回新 Access Token(可选旋转 Refresh Token)
    • 失败:返回错误(403 Forbidden),用户需要重新登录
  6. 重试或终结

    • 刷新成功:自动重发失败请求,用户无感知
    • 刷新失败:清除认证信息,重定向登录页面

3. 实战 Demo:Axios 拦截器实现无感刷新

以下示例展示了 处理并发请求、自动刷新 Token 并重试失败请求 的完整方案。

3.1 创建 Axios 实例

// api/request.js
import axios from 'axios';
import { refreshTokenApi } from './auth';
import { getAccessTokenFromStore, setAccessTokenInStore, logoutUser } from './authStore';

const service = axios.create({
  baseURL: '/api',
  timeout: 10000,
});

// 请求拦截器:每个请求自动带上 Access Token
service.interceptors.request.use(
  config => {
    const accessToken = getAccessTokenFromStore();
    if (accessToken) {
      config.headers['Authorization'] = `Bearer ${accessToken}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

3.2 响应拦截器:处理无感刷新

let isRefreshing = false;        // 刷新状态标志
let requestsQueue = [];          // 挂起请求队列

service.interceptors.response.use(
  response => response,
  async error => {
    const { config, response } = error;
    const status = response?.status;

    if (status !== 401) return Promise.reject(error);

    // 如果正在刷新 Token,先挂起请求
    if (isRefreshing) {
      return new Promise(resolve => {
        requestsQueue.push(() => resolve(service(config)));
      });
    }

    isRefreshing = true;

    try {
      // 调用刷新 Token 接口
      const { newAccessToken } = await refreshTokenApi();

      // 更新本地 Access Token
      setAccessTokenInStore(newAccessToken);

      // 重试刚才失败的请求
      config.headers['Authorization'] = `Bearer ${newAccessToken}`;
      requestsQueue.forEach(cb => cb());
      requestsQueue = [];

      return service(config);

    } catch (refreshError) {
      console.error('Refresh Token 失效,请重新登录', refreshError);
      logoutUser();  // 清理并重定向登录页
      return Promise.reject(refreshError);
    } finally {
      isRefreshing = false;
    }
  }
);

export default service;

3.3 核心要点解析

  1. 并发处理

    • isRefreshing + requestsQueue 防止多个请求同时触发刷新,保证原子性。
  2. 无感刷新

    • 用户操作不中断,失败请求会自动重试。
  3. 优雅降级

    • Refresh Token 失效时自动登出,避免死循环或安全风险。

4. 总结

无感刷新 Token 是现代 Web 应用提升用户体验的 标配方案

  • 用户几乎感受不到登录过期
  • API 安全性得到保障
  • 支持并发请求,避免刷新竞态问题
  • 支持 Refresh Token 旋转策略,提高安全性

实现这一机制不仅是几行代码,更是对 认证流程、安全性、用户体验 三者之间平衡的深刻理解。

推荐文章

Nginx 反向代理 Redis 服务
2024-11-19 09:41:21 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
55个常用的JavaScript代码段
2024-11-18 22:38:45 +0800 CST
2024年微信小程序开发价格概览
2024-11-19 06:40:52 +0800 CST
五个有趣且实用的Python实例
2024-11-19 07:32:35 +0800 CST
CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
Python实现Zip文件的暴力破解
2024-11-19 03:48:35 +0800 CST
Vue中如何使用API发送异步请求?
2024-11-19 10:04:27 +0800 CST
Go语言中的`Ring`循环链表结构
2024-11-19 00:00:46 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
利用Python构建语音助手
2024-11-19 04:24:50 +0800 CST
Graphene:一个无敌的 Python 库!
2024-11-19 04:32:49 +0800 CST
如何在Rust中使用UUID?
2024-11-19 06:10:59 +0800 CST
php strpos查找字符串性能对比
2024-11-19 08:15:16 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
pip安装到指定目录上
2024-11-17 16:17:25 +0800 CST
imap_open绕过exec禁用的脚本
2024-11-17 05:01:58 +0800 CST
10个极其有用的前端库
2024-11-19 09:41:20 +0800 CST
解决 PHP 中的 HTTP 请求超时问题
2024-11-19 09:10:35 +0800 CST
四舍五入五成双
2024-11-17 05:01:29 +0800 CST
程序员茄子在线接单