前端无感刷新 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;
相关推荐
计算机魔术师21 小时前
OpenAI 发布 GPT-6 Astra:多项基准刷新纪录, cybersecurity 能力达 Critical 阈值
前端
xy345321 小时前
Axure9.0中继器遮罩实现方法
前端·ui·html·axure·原型·产品设计
风骏时光牛马1 天前
智能任务自动化协同AI工作流
前端
IT_陈寒1 天前
Python的多线程居然是个假把式?搞清GIL让我少熬三天夜
前端·人工智能·后端
宿6741 天前
vue3-config
前端·javascript·vue.js
明月_清风1 天前
位图与布隆过滤器:海量数据下的"存在性判断"艺术
前端·后端·算法
明月_清风1 天前
Hash 表从入门到精通:Go 实战与工程细节
前端·后端·算法
非凡ghost1 天前
AI修图不“造假“,摄影师的出片效率加速器
服务器·前端·人工智能·电脑
2601_951615201 天前
网页设计模板源码 web前端模板网页源码下载
前端
mmsx1 天前
osmdroid 地图实战 03|地图的"分层世界":离线方案、图层模型与 Overlay 体系
android·前端