在alovajs中实现Token无感刷新机制

一、背景与核心需求

在前后端分离架构中,Token作为身份认证的核心凭证,其生命周期管理直接影响系统安全性与用户体验。传统Token机制存在两大痛点:

  1. 过期中断:用户操作过程中Token过期,导致请求失败,需手动重新登录。
  2. 体验割裂:显式跳转登录页或弹窗提示会打断用户操作流程。

无感刷新的核心目标是在Token即将过期时,自动完成续期并重试失败请求,使用户无感知地维持会话状态。alovajs作为轻量级前端框架,需通过拦截器、状态管理等技术实现这一机制。

二、无感刷新机制的核心原理

1. Token生命周期管理

  • 双Token设计
    • Access Token:短期有效(如2小时),用于访问API。
    • Refresh Token:长期有效(如7天),用于获取新的Access Token。
  • 过期时间阈值
    设定提前续期的阈值(如剩余30分钟),避免请求因Token过期而失败。

2. 请求拦截与重试

  • 拦截器逻辑
    在alovajs中通过封装axios或自定义请求库,拦截所有API请求。
    • 检查Access Token是否有效(剩余时间>阈值)。
    • 若无效,使用Refresh Token获取新Token,并重试原请求。
  • 错误分类处理
    • 401未授权:触发Token刷新流程。
    • 403禁止访问:直接终止请求(非Token问题)。
    • 网络错误:按重试策略处理。

三、alovajs中的实现步骤

1. 封装请求工具类

  1. // src/utils/request.js
  2. import axios from 'axios';
  3. const service = axios.create({
  4. baseURL: 'https://api.example.com',
  5. timeout: 5000
  6. });
  7. // 请求拦截器
  8. service.interceptors.request.use(
  9. (config) => {
  10. const token = localStorage.getItem('accessToken');
  11. if (token) {
  12. config.headers['Authorization'] = `Bearer ${token}`;
  13. }
  14. return config;
  15. },
  16. (error) => Promise.reject(error)
  17. );
  18. // 响应拦截器
  19. service.interceptors.response.use(
  20. (response) => response,
  21. async (error) => {
  22. const { response } = error;
  23. if (response?.status === 401) {
  24. try {
  25. const refreshToken = localStorage.getItem('refreshToken');
  26. const newToken = await refreshAccessToken(refreshToken); // 调用刷新接口
  27. localStorage.setItem('accessToken', newToken.accessToken);
  28. // 重试原请求
  29. return service(error.config);
  30. } catch (refreshError) {
  31. // 刷新失败,跳转登录
  32. window.location.href = '/login';
  33. return Promise.reject(refreshError);
  34. }
  35. }
  36. return Promise.reject(error);
  37. }
  38. );
  39. async function refreshAccessToken(refreshToken) {
  40. const res = await axios.post('/auth/refresh', { refreshToken });
  41. return res.data;
  42. }
  43. export default service;

2. Token存储与过期检测

  • 存储方案
    使用localStoragesessionStorage存储Token,避免内存泄漏。
  • 过期时间计算
    解析Token中的exp字段(Unix时间戳),计算剩余有效期。
    1. function isTokenExpired(token) {
    2. const payload = JSON.parse(atob(token.split('.')[1]));
    3. const expiresAt = payload.exp * 1000; // 转换为毫秒
    4. return Date.now() >= expiresAt;
    5. }

3. 自动刷新触发条件

  • 前置检查
    在发起请求前,检查Access Token剩余时间是否小于阈值(如30分钟)。
  • 静默刷新
    即使无请求,也可通过定时任务提前刷新Token(需权衡性能)。

四、优化策略与最佳实践

1. 并发请求处理

  • 锁机制
    当多个请求同时触发Token刷新时,避免重复调用刷新接口。

    1. let isRefreshing = false;
    2. let subscribers = [];
    3. service.interceptors.response.use(
    4. (response) => response,
    5. async (error) => {
    6. if (error.response?.status === 401) {
    7. if (!isRefreshing) {
    8. isRefreshing = true;
    9. const refreshToken = localStorage.getItem('refreshToken');
    10. try {
    11. const newToken = await refreshAccessToken(refreshToken);
    12. localStorage.setItem('accessToken', newToken.accessToken);
    13. subscribers.forEach((cb) => cb(newToken.accessToken));
    14. subscribers = [];
    15. } finally {
    16. isRefreshing = false;
    17. }
    18. }
    19. // 等待新Token
    20. return new Promise((resolve) => {
    21. subscribers.push((token) => {
    22. error.config.headers['Authorization'] = `Bearer ${token}`;
    23. resolve(service(error.config));
    24. });
    25. });
    26. }
    27. return Promise.reject(error);
    28. }
    29. );

2. 安全性增强

  • Refresh Token轮换
    每次刷新后生成新的Refresh Token,避免固定Token长期有效。
  • HTTPS与CSRF防护
    确保刷新接口仅通过HTTPS访问,并添加CSRF Token。

3. 性能优化

  • 缓存Token
    将Token解析后的payload缓存,减少重复解码开销。
  • 请求合并
    对短时间内密集请求进行合并,减少刷新次数。

五、常见问题与解决方案

1. Refresh Token被盗用

  • 解决方案
    • 设置Refresh Token的短期有效期(如1天)。
    • 结合设备指纹或IP限制,检测异常刷新行为。

2. 移动端网络不稳定

  • 解决方案
    • 增加重试次数(如最多3次)。
    • 在无网络时缓存请求,待网络恢复后自动重试。

3. 多标签页同步

  • 解决方案
    使用BroadcastChannellocalStorage事件监听,实现多标签页Token状态同步。

六、总结与展望

无感刷新机制通过自动化Token管理,显著提升了前端应用的流畅性与安全性。在alovajs中的实现需重点关注拦截器设计、并发控制及错误处理。未来可结合Service Worker实现离线场景下的请求缓存与重试,进一步优化用户体验。