前端无感刷新 Token 的 Axios 封装方案

在现代前端应用中,基于 Token 的身份验证已成为主流方案。然而,Token 过期问题常常困扰开发者 ------ 如何在不打断用户操作的情况下自动刷新 Token,实现 "无感刷新" 体验?本文将详细介绍基于 Axios 的解决方案。

什么是无感刷新 Token?

无感刷新 Token 指的是当用户访问需要身份验证的接口时,若当前 Token 已过期,系统自动使用刷新 Token 获取新的访问 Token,并用新 Token 重新发起原请求,整个过程对用户完全透明,不影响用户操作流程。

实现思路
  1. 拦截所有请求,在请求头中自动添加 Token
  2. 拦截响应,检测 Token 过期错误
  3. 当 Token 过期时,使用刷新 Token 获取新的访问 Token
  4. 用新 Token 重新发起原请求,并将结果返回给用户
  5. 处理并发请求问题,避免多次刷新 Token

代码

auth.js:

javascript 复制代码
// Token存储工具函数

// 存储Token到本地存储
export const setToken = (token) => {
  localStorage.setItem('accessToken', token);
};

// 从本地存储获取Token
export const getToken = () => {
  return localStorage.getItem('accessToken');
};

// 存储刷新Token到本地存储
export const setRefreshToken = (refreshToken) => {
  localStorage.setItem('refreshToken', refreshToken);
};

// 从本地存储获取刷新Token
export const getRefreshToken = () => {
  return localStorage.getItem('refreshToken');
};

// 清除所有Token
export const removeTokens = () => {
  localStorage.removeItem('accessToken');
  localStorage.removeItem('refreshToken');
};

request.js:

javascript 复制代码
import axios from 'axios';
import { getToken, getRefreshToken, setToken, removeTokens } from './auth';

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 基础URL
  timeout: 5000 // 请求超时时间
});

// 是否正在刷新的标记
let isRefreshing = false;
// 存储等待刷新的请求队列
let requests = [];

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 从本地存储获取Token
    const token = getToken();
    // 如果Token存在,则添加到请求头
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    // 请求错误处理
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 成功响应处理
    return response.data;
  },
  async error => {
    const originalRequest = error.config;
    
    // 如果不是401错误或者已经重试过,则直接返回错误
    if (error.response?.status !== 401 || originalRequest._retry) {
      return Promise.reject(error);
    }
    
    // 如果正在刷新Token,则将请求加入队列
    if (isRefreshing) {
      try {
        // 等待刷新Token完成
        const token = await new Promise(resolve => {
          requests.push(token => {
            resolve(token);
          });
        });
        // 使用新Token重新发起请求
        originalRequest.headers['Authorization'] = `Bearer ${token}`;
        return service(originalRequest);
      } catch (err) {
        return Promise.reject(err);
      }
    }
    
    // 标记为正在刷新Token
    originalRequest._retry = true;
    isRefreshing = true;
    
    try {
      // 调用刷新Token接口
      const refreshToken = getRefreshToken();
      const { data } = await axios.post(`${process.env.VUE_APP_BASE_API}/refresh-token`, {
        refreshToken
      });
      
      // 存储新的Token
      setToken(data.token);
      
      // 执行队列中的请求
      requests.forEach(cb => cb(data.token));
      requests = [];
      
      // 重新发起原请求
      originalRequest.headers['Authorization'] = `Bearer ${data.token}`;
      return service(originalRequest);
    } catch (refreshError) {
      // 刷新Token失败,清除Token并跳转登录页
      removeTokens();
      window.location.href = '/login';
      return Promise.reject(refreshError);
    } finally {
      // 重置刷新状态
      isRefreshing = false;
    }
  }
);

export default service;
相关推荐
卓码软件测评12 小时前
第三方软件登记测试机构:【软件登记测试机构HTML5测试技术】
前端·功能测试·测试工具·html·测试用例·html5
CS Beginner12 小时前
【html】canvas实现一个时钟
前端·html
林烈涛12 小时前
js判断变量是数组还是对象
开发语言·前端·javascript
Komorebi_999913 小时前
Unocss
开发语言·前端
goto_w14 小时前
前端实现复杂的Excel导出
前端·excel
Baklib梅梅14 小时前
2025文档管理软件推荐:效率、安全与协作全解析
前端·ruby on rails·前端框架·ruby
卷Java14 小时前
小程序前端功能更新说明
java·前端·spring boot·微信小程序·小程序·uni-app
FogLetter14 小时前
前端性能救星:虚拟列表原理与实现,让你的10万条数据流畅如丝!
前端·性能优化
我是天龙_绍14 小时前
前端驼峰,后端下划线,问:如何统一?
前端
知识分享小能手15 小时前
微信小程序入门学习教程,从入门到精通,微信小程序常用API(下)——知识点详解 + 案例实战(5)
前端·javascript·学习·微信小程序·小程序·vue·前端开发