封装Axios拦截器实现用户无感刷新AccessToken实践指南

news2025/3/13 8:15:37

一、背景与需求场景

1.1 单点登录体系中的Token管理

在单点登录(SSO)体系下,用户登录后系统会颁发两种令牌:

  • AccessToken:短期有效(2小时),用于接口鉴权

  • RefreshToken:长期有效(7天),用于刷新令牌

1.2 核心需求痛点

当AccessToken过期时,前端需要实现:

  1. 无感知刷新:用户操作不中断

  2. 并发控制:避免多个请求重复刷新

  3. 异常处理:RefreshToken失效时跳转登录


二、技术实现方案

2.1 整体实现思路

采用响应拦截器方案,当接口返回401时触发刷新机制:

2.2 核心代码实现

2.2.1 初始化Axios实例
const instance = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 10000
})
2.2.2 响应拦截器实现
// 刷新状态标记,用于记录当前是否正在进行刷新 token 的操作
// 避免在刷新 token 过程中重复发起刷新请求
let isRefreshing = false;
// 请求等待队列,用于存储在刷新 token 期间被拦截的请求
// 当新的 access_token 获取成功后,会依次处理这些请求
let requests = [];

// 为 axios 实例添加响应拦截器
// 响应拦截器会在每次请求响应回来时触发,可用于对响应进行统一处理
instance.interceptors.response.use(
  // 当响应成功时,直接返回响应对象,不做额外处理
  response => response,
  // 当响应出现错误时的处理逻辑
  error => {
    // 从错误对象中解构出响应对象和请求配置对象
    const { response, config } = error;

    // 判断响应状态码是否为 401(未授权),并且请求的 URL 不包含 '/auth/refresh'
    // 排除刷新 token 请求本身出现 401 错误的情况,避免无限循环
    if (response.status === 401 && !config.url.includes('/auth/refresh')) {
      // 如果当前没有正在进行刷新 token 的操作
      if (!isRefreshing) {
        // 标记正在进行刷新 token 的操作
        isRefreshing = true;
        // 调用刷新 token 的函数
        return refreshToken()
          .then(({ access_token }) => {
            // 刷新 token 成功,将新的 access_token 存储起来
            setToken(access_token);
            // 更新当前请求的请求头,使用新的 access_token
            config.headers.Authorization = `Bearer ${access_token}`;
            // 遍历请求等待队列,依次执行队列中的回调函数,并传入新的 access_token
            requests.forEach(cb => cb(access_token));
            // 清空请求等待队列
            requests = [];
            // 使用更新后的请求配置重新发起请求,并返回请求结果
            return instance(config);
          })
          .catch(() => {
            // 刷新 token 失败,调用登出函数,可能会清除本地存储的 token 并重定向到登录页
            logout();
          })
          .finally(() => {
            // 无论刷新 token 成功还是失败,都将刷新状态标记设置为 false,表示刷新操作结束
            isRefreshing = false;
          });
      }
      // 如果当前正在进行刷新 token 的操作
      return new Promise(resolve => {
        // 将当前请求封装成一个回调函数,加入到请求等待队列中
        requests.push(newToken => {
          // 当新的 access_token 获取成功后,更新当前请求的请求头
          config.headers.Authorization = `Bearer ${newToken}`;
          // 使用更新后的请求配置重新发起请求,并将结果传递给 resolve 函数
          resolve(instance(config));
        });
      });
    }
    // 如果不是 401 错误或者是刷新 token 请求本身出现错误,直接将错误对象抛出
    return Promise.reject(error);
  }
);

2.3 关键代码解析

代码部分功能说明
isRefreshing防止重复刷新的状态锁
requests存储等待中的请求队列
config.url.includes排除刷新接口本身
requests.forEach令牌刷新后重发所有挂起请求
new Promise创建待处理的Promise对象

三、方案优化点

3.1 防止并发刷新

通过isRefreshing状态锁实现:

if (!isRefreshing) {
  isRefreshing = true
  // 刷新逻辑...
}

3.2 请求队列处理

使用闭包存储请求配置:

requests.push(newToken => {
  config.headers.Authorization = `Bearer ${newToken}`
  resolve(instance(config))
})

3.3 安全增强措施

  1. 加密存储Token到Cookie

  2. 设置HttpOnly属性

  3. 启用Secure传输


四、方案对比分析

方案优点缺点
请求拦截器方案节省无效请求依赖本地时间准确性
响应拦截器方案时间判断更可靠多一次失败请求
服务端代理方案前端完全无感知需要服务端支持
WebSocket方案实时性最好实现复杂,资源消耗大

五、完整实现代码

// utils/request.js
import axios from 'axios'

let isRefreshing = false
let requests = []

const instance = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 10000
})

// 响应拦截器
instance.interceptors.response.use(
  response => response,
  error => {
    if (!error.response) return Promise.reject(error)
    
    const { status, config } = error.response
    if (status === 401 && !config._retry) {
      config._retry = true
      
      if (!isRefreshing) {
        isRefreshing = true
        return refreshToken()
          .then(({ access_token }) => {
            setToken(access_token)
            instance.defaults.headers.common['Authorization'] = `Bearer ${access_token}`
            requests.forEach(cb => cb(access_token))
            requests = []
            return instance(config)
          })
          .catch(err => {
            clearToken()
            redirectLogin()
            return Promise.reject(err)
          })
          .finally(() => isRefreshing = false)
      }
      
      return new Promise(resolve => {
        requests.push(token => {
          config.headers['Authorization'] = `Bearer ${token}`
          resolve(instance(config))
        })
      })
    }
    return Promise.reject(error)
  }
)

// 刷新Token方法
async function refreshToken() {
  try {
    const { refresh_token } = getToken()
    return await axios.post('/auth/refresh', { refresh_token })
  } catch (err) {
    throw new Error('刷新令牌失败')
  }
}

六、总结与展望

本文实现的方案具有以下优势:

  1. 用户无感知:自动处理令牌刷新

  2. 高可靠性:完善的错误处理机制

  3. 高性能:请求队列避免重复刷新

后续优化方向:

  1. 添加请求重试次数限制

  2. 实现滑动过期时间

  3. 增加网络异常处理

  4. 结合WebSocket实时更新

通过合理的前端Token管理策略,可以显著提升用户体验,降低重复登录频率。本方案已在生产环境稳定运行,可满足大多数SSO场景需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSDN博客:Markdown编辑语法教程总结教程(下)

❤个人主页:折枝寄北的博客 Markdown编辑语法教程总结 前言1. LaTex数学公式2. 插入不同类别的图2.1 插入甘特图2.2 插入UML图2.3 插入Mermaid流程图2.4 插入Flowchart流程图2.5 插入classDiagram类图 3. CSDN快捷键4. 字体相关设置4.1 字体样式改变4.2 字体大小改变…

【Python】06、流程控制语句

文章目录 1.条件判断语句1.1 if 语句2. input 函数3.if-else 语句4.if-elif-else 语句 2.循环语句2.1 while语句2.2 while语句练习:2.3 循环嵌套2.4 break和continue 通过流程控制语句,可以改变程序的执行顺序,也可以让指定程序反复执行多次。…

《python》—— threading库(线程和多线程)

文章目录 threading简介threading基本概念常用类和方法线程同步线程池实例 threading简介 threading 是 Python 标准库中用于实现多线程编程的模块。多线程编程允许程序同时执行多个任务,提高程序的并发性能,尤其适用于 I/O 密集型任务,例如…

【数据分享】2000-2024年全国逐年归一化植被指数(NDVI)栅格数据(年最大值)

NDVI,全名为Normalized Difference Vegetation Index,中文名称为归一化植被指数。这个指数可以用来定性和定量评价植被覆盖及其生长活力,我们也可以简单地将它理解为体现植被密度和健康状况的一个指标。 之前我们给大家分享了来源于MOD13A3数…

混沌理论与混沌映射——算法改进初始化创新点之一

混沌理论与混沌映射 混沌理论研究混沌系统的动力学,其特征是非线性和对初始条件的极端敏感性。即使在这些条件下的微小变化也可能导致系统结果的显著变化。尽管看起来是随机的,混沌系统可以在不依赖随机性的情况下表现出不规则的行为,因为确…

19874并查集

19874并查集 ⭐️难度:中等 🌟考点:并查集、数据结构 📖 📚 import java.util.*;public class Main {static int N 100010;static int[] a new int[N];static int[] p new int[N];static int n;static int m;st…

Jmeter下载安装配置及使用

1、下载 官网地址:Apache JMeter - Download Apache JMeter 2、配置环境变量 ①找到环境变量,两种方法 法一:我的电脑→右键菜单→属性→高级系统设置→环境变量 法二:直接搜索环境变量 ②新建两个系统变量 1.变量名&#x…

【从零开始学习计算机科学】编译原理(一)编译过程概述

【从零开始学习计算机科学】编译原理(一)编译过程概述 绪论编译过程概述词法分析语法分析代码优化代码生成其他功能编译器的前端和后端绪论 什么叫编译程序?为什么我们需要编译程序?编译程序就是一个程序,将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻…

【算法day8】 Z 字形变换 -O(n)算法思路整理

Z 字形变换,算法思路整理 https://leetcode.cn/problems/zigzag-conversion/description/ 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下…

L3-1 夺宝大赛

输入样例 1: 5 7 1 1 1 1 1 0 1 1 1 1 1 1 0 0 1 1 0 2 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 7 1 5 7 1 1 1 5 5 3 1 3 5 1 4输出样例 1: 7 6样例 1 说明: 七支队伍到达大本营的时间顺次为:7、不可能、5、3、3、5、6&#xff0c…

Matlab:矩阵运算篇——矩阵

目录 1.定义 实例——创建矩阵 实例——创建复数矩阵 2.矩阵的生成 实例——M文件矩阵 2.利用文本创建 实例——创建生活用品矩阵 3.创建特殊矩阵 实例——生成特殊矩阵 4.矩阵元素的运算 1.矩阵元素的修改 实例——新矩阵的生成 2.矩阵的变维 实例——矩阵维度修…

泛微ecode的页面开发发送请求参数携带集合

1.在开发过程中我们难免遇见会存在需要将集合传递到后端的情况,那么这里就有一些如下的注意事项,如以下代码: // 新增action.boundasync addQuestion(formData) {var theList this.questionAnswerList;var questionAnswerListArray new Ar…

Javaweb后端全局异常处理器

类名随便定义 这是异常处理的方法exceptionhandler responsebody作用,方法的响应值返回给前端,如果返回的是集合对象,会把集合对象转为json,再给前端响应返回

SpringBoot缓存抽象:@Cacheable与缓存管理器配置

文章目录 引言一、SpringBoot缓存抽象概述二、Cacheable注解详解2.1 Cacheable的关键属性 三、缓存管理器配置四、自定义键生成策略五、缓存同步与失效策略六、SpringBoot缓存最佳实践总结 引言 缓存是提升应用性能的关键技术,SpringBoot提供了强大的缓存抽象层&am…

江科大51单片机笔记【11】AT24C02(I2C总线)

一、存储器 1.介绍 RAM的特点是存储速度特别快,但是掉电会丢失;ROM的特点是存储速度特别慢,但是掉电不会丢失 SRAM是所有存储器最快的,一般用于电脑的CPU高速缓存,容量相对较少,成本较高;DRAM…

外层元素旋转,其包括在内的子元素一并旋转(不改变旋转中心),单元测试

思路&#xff1a;外层旋转后坐标&#xff0c;元素旋转后坐标&#xff0c;计算偏移坐标 <template><div class"outbox"><label>角度: <input v-model.number"rotate" type"number" /></label><br><div c…

Docker容器安装软件(完整版)

文章目录 一、安装Docker1.1 docker 相关的命令1.2 配置镜像加速 二. 安装es2.1 创建网络2.2 拉取镜像2.3 创建挂载点目录2.4 部署单点es&#xff0c;创建es容器2.5 编写elasticsearch.yml2.6 重启es容器2.7 测试Elasticsearch是否安装成功 三. 基于Docker安装Kibana3.1 拉取镜…

「 机器人 」扑翼飞行器通过总气动力控制四自由度运动方法

一、前言 在扑翼飞行中,总气动力(Total Aerodynamic Force)是指扑翼在运动过程中受到的所有空气动力作用的合力。它是由以下两种主要力的合成结果: 1. 升力(Lift, ):垂直于空气流方向的力,用于支持飞行器(或生物)的重量。 2. 阻力(Drag, ):平行于空气流方向的力,…

Axios简单说明,快速上手

Ajax&#xff1a;异步的JavaScript和XML 作用&#xff1a; 数据交换异步交互 Axios&#xff1a;就是对原生Ajax进行封装&#xff0c;简化书写&#xff0c;快速开发 使用逻辑&#xff1a; 首先要安装Axios&#xff0c;可以通过npm在项目中安装&#xff1a; 打开命令行工具…

云服务器安装宝塔面板部署

单机部署(前端vue项目) 服务器安装宝塔面板 连接到服务器 使用 SSH 连接到你的服务器&#xff1a; ssh rootip安装宝塔面板 运行以下命令来安装宝塔面板&#xff1a; yum install -y wget wget -O install.sh http://download.bt.cn/install/install_6.0.sh sh install.sh安…