告别登录中断:前端双 Token无感刷新

作者:发现一只大呆瓜日期:2026/3/16

前言

在前后端分离的项目中,为了安全,Token 通常会设置有效期。但如果 Token 过期时强制用户重新登录,会极大地破坏用户体验。如何做到在用户毫无察觉的情况下,自动完成 Token 的续期?本文将深度拆解 “双 Token 无感刷新” 的实现机制。

一、 为什么需要“无感刷新”?

举个简单例子,你正在某 App 编辑内容,中途切出几分钟,再切回来时,直接弹出登录页,提示“登录已过期,请重新登录”,这种场景很容易让用户流失。

传统的单 Token 方案存在一个两难境地:

  • 有效期过短:用户操作频繁,动不动就跳回登录页,用户体验极差。
  • 有效期过长:Token 一旦被截获,风险极高。

解决方案:双 Token 机制

  1. access_token:访问令牌。有效期短(如 1 小时),每次接口请求都携带,降低泄露风险。
  2. refresh_token:刷新令牌。有效期长(如 7 天),仅用于 access_token 过期时换取新令牌。

只要用户在 7 天内活跃过,系统就能通过 refresh_token 自动“续命”,实现长效无感登录。


二、 核心流程设计

  1. 正常请求:前端携带 access_token 访问。
  2. 触发过期:后端返回 401 Unauthorized
  3. 判断逻辑
    • 如果是普通接口报 401:说明 access_token 失效,尝试刷新。
    • 如果是刷新接口报 401:说明 refresh_token 也失效了,强制重新登录。
  4. 无感替换:前端自动调用刷新接口,获取新 Token 覆盖本地存储,并重新发起之前失败的请求。

三、 细节攻坚:如何处理并发请求?

痛点:如果页面同时发出了 5 个请求,而此时 Token 刚好过期,会导致这 5 个请求同时触发“刷新 Token”的操作,造成资源浪费甚至后端异常。

解决策略

  • 状态锁 (refreshing) :记录当前是否正在刷新中。
  • 任务队列 (queue) :在刷新期间到达的请求,先暂存起来,不直接报错。
  • 批量回放:等待 Token 刷新成功后,依次执行队列里的请求,实现“无感”衔接。

四、 代码实现 (Axios 拦截器)

以下是基于 Axios 的完整工程化实现:

1import axios, { AxiosRequestConfig } from 'axios';
2
3interface PendingTask {
4    config: AxiosRequestConfig;
5    resolve: Function;
6}
7
8let refreshing = false; // 状态锁:标志是否正在刷新 Token
9let queue: PendingTask[] = []; // 请求队列:暂存 Token 刷新期间的请求
10
11const axiosInstance = axios.create({
12    baseURL: '/api'
13});
14
15// 1. 请求拦截器:自动注入 Token
16axiosInstance.interceptors.request.use((config) => {
17    const accessToken = localStorage.getItem('access_token');
18    if (accessToken && config.headers) {
19        config.headers.authorization = `Bearer ${accessToken}`;
20    }
21    return config;
22});
23
24// 2. 响应拦截器:处理 Token 过期
25axiosInstance.interceptors.response.use(
26    (response) => response,
27    async (error) => {
28        const { data, config } = error.response;
29
30        // 情况 A:正在刷新 Token 中,将后续请求存入队列
31        if (refreshing) {
32            return new Promise((resolve) => {
33                queue.push({ config, resolve });
34            });
35        }
36
37        // 情况 B:access_token 过期 (状态码 401 且非刷新接口本身)
38        if (data.statusCode === 401 && !config.url.includes('/refresh')) {
39            refreshing = true;
40            
41            try {
42                const res = await refreshToken();
43                refreshing = false;
44
45                if (res.status === 200) {
46                    // 核心逻辑:Token 刷新成功,回放队列中的所有请求
47                    queue.forEach(({ config, resolve }) => {
48                        resolve(axiosInstance(config));
49                    });
50                    queue = []; // 清空队列
51                    
52                    // 执行当前触发刷新的那个请求
53                    return axiosInstance(config);
54                }
55            } catch (err) {
56                refreshing = false;
57                queue = [];
58                // 情况 C:refresh_token 也过期了,彻底清除登录态
59                localStorage.clear();
60                window.location.href = '/login';
61                return Promise.reject(err);
62            }
63        }
64
65        return Promise.reject(error);
66    }
67);
68
69/**
70 * 刷新 Token 的异步方法
71 */
72async function refreshToken() {
73    const res = await axios.get('/api/refresh', {
74        params: {
75            token: localStorage.getItem('refresh_token')
76        }
77    });
78    // 更新本地存储
79    localStorage.setItem('access_token', res.data.accessToken);
80    localStorage.setItem('refresh_token', res.data.refreshToken);
81    return res;
82}
83

五、 注意事项

  1. 并发请求的 Promise 挂起:在 refreshingtrue 时,返回一个不带 resolvenew Promise 是关键,它能让 Axios 请求处于 pending 状态。
  2. 错误捕获refreshToken 接口本身报错(如 500 或 401)必须妥善处理,直接引导至登录页。
  3. 安全性:普通项目中可以使用 localStorage,但在更高要求的项目中,建议配合 HttpOnly Cookie 存储 refresh_token 以防 XSS 攻击。
  4. 接口重定向陷阱:确保刷新 Token 的接口不会再次进入 401 拦截死循环。

告别登录中断:前端双 Token无感刷新》 是转载文章,点击查看原文


相关推荐


构建无障碍组件之Switch Pattern
anOnion2026/3/8

Switch Pattern 详解:构建无障碍开关组件 开关(Switch)是一种模拟物理开关的控件,用于在两个状态(通常是"开"和"关")之间切换。在一些 UI 组件库中,它也被称为 Toggle(切换开关)。本文基于 W3C WAI-ARIA Switch Pattern 规范,详解如何构建无障碍的开关组件。 一、Switch 的定义与核心概念 1.1 什么是 Switch Switch 是一种特殊的二元状态控件,它: 模拟物理开关的行为 在两个互斥状态之间切换(开/关、启用/禁用) 与


纯 CSS 实现弹性文字效果
掘金安东尼2026/2/28

原文:How to Create a CSS-only Elastic Text Effect 翻译:TUARAN 欢迎关注 前端周刊,每周更新国外论坛的前端热门文章,紧跟时事,掌握前端技术动态。 每个字母单独动画的文字效果总是很酷、很吸睛。这类错峰动画通常依赖 JavaScript 库实现,对我们要实现的这种相对轻量的设计效果来说,代码往往偏重。本文将探索只用 CSS、无需 JavaScript 实现 fancy 文字效果的技巧(意味着需要手动拆分字符)。 截至撰写时,仅 Chrome 和


LiteOps:轻量级CI/CD平台,重塑开发运维新体验
YL_jia2026/2/19

LiteOps:轻量级CI/CD平台,重塑开发运维新体验 在效率至上的时代,LiteOps正以“简洁易用”和“开箱即用”的理念,重新定义自动化部署流程。 一、LiteOps:为何成为开发运维的新宠? 在软件开发的快速迭代中,持续集成和持续部署(CI/CD) 已成为提升开发效率和软件质量的关键手段。 然而传统CI/CD工具往往配置复杂、学习曲线陡峭。LiteOps作为一个专注于实用性的轻量级CI/CD平台,应运而生,它开源免费,能够为开发团队提供高效、便捷的自动化构建和部署解决方案。 1.1


BLE协议栈:链路层与ATT/L2CAP的交互详解
mftang2026/2/11

目录 概述 1  整体交互架构概览 1.1 交互流程总览 1.2 数据平面:PDU传输流程 1.2.1  发送路径:从ATT到空中 1.2.2 接收路径:从空中到ATT 1.3 控制平面:连接与参数管理 1.3.1 连接生命周期交互 1.3.2 关键参数协商流程 1.4 事件与通知机制 1.4.1 链路层事件驱动模型 1.4.2  ATT通知/指示与链路层交互 1.5 性能优化交互 1.5.1 数据长度扩展交互(BLE 4.2+) 1.5.2 信道选择算法 1


2026 年或许是中国 AI 社交的元年~
苍何2026/2/2

这是苍何的第 478 篇原创! 大家好,我是苍何。 网上你们经常在哪儿聊天啊? 我妈说在抖音,我弟说在陌陌,但其实我们都是在微信里聊的这个问题。 我自己建了几十个微信群,还加入了一堆的群聊,每天光看信息,就得费我半天老命。 为此,我基于 Ipad 协议开发过微信 AI 助手,能总结群聊消息,能帮回复问题,帮做任务,2021 年卖给了麻省理工学院社团。 一开始,我以为,我能用 AI 助手解决这些问题,但后面,还是避免不了被各种封号,最痛苦的是,我的群直接就被一锅端了。 所以,后面,我没再折腾微信


多网卡如何区分路由,使用宽松模式测试网络
venus602026/1/23

一、什么是 Linux 的“非对称路由” 1️⃣ 定义(先给结论) 非对称路由指的是: 数据包从 A 网卡进来,但回包却从 B 网卡出去 在多网卡、多出口服务器上非常常见,比如: 双网卡 多默认网关 同一台服务器连多个网络 你之前的情况就是典型的非对称路由。 2️⃣ Linux 默认为什么不喜欢非对称路由? 因为它可能意味着: IP 欺骗(spoofing) 流量劫持 路由异常 所以 Linux 默认启用了一个安全机制: 👉


三分钟说清楚 ReAct Agent 的技术实现
indieAI2026/1/15

ReAct Agent 技术实现主要依赖于精心设计的 Prompt 模板、输出解析器和执行循环三大核心机制。 1. 核心 Prompt 工程 LangChain 使用特定的 Prompt 模板引导 LLM 按 Thought → Action → Observation 格式输出: # 简化的 Prompt 结构 template = """ 用以下工具回答问题: 工具: - search: 搜索引擎, 输入: "查询词" - calculator: 计算器, 输入: "算式" 现在开始


基于 Squoosh WASM 的浏览器端图片转换库
jump_jump2026/1/7

在 Web 开发中,图片处理是一个常见需求。传统方案要么依赖服务端处理,要么使用 Canvas API,但前者增加服务器负担,后者在压缩质量上不尽人意。Google 的 Squoosh 项目提供了基于 WASM 的高质量图片编解码器,但直接使用比较繁琐。 于是我封装了 use-squoosh,一个零依赖的浏览器端图片转换库,通过 CDN 按需加载编解码器,开箱即用。 为什么需要这个库 现有方案的局限性 方案优点缺点服务端处理稳定可靠增加


微调—— LlamaFactory工具:使用WebUI微调
华如锦2025/12/29

启动web Ui面板 进入到LLaMA-Factory目录下,执行以下命令启动web ui面板: cd LLaMA-Factory llamafactory-cli webui llamafactory-cli webui 进入web ui面板 微调前准备 1. 数据准备 LLaMA-Factory 自带数据集以 .json 格式存放在项目根目录的 LLaMA-Factory/data 文件夹中,在图形化微调界面中可直接通过下拉框选择这些数据集。)。


Python入门指南(五) - 为什么选择 FastAPI?
吴佳浩2025/12/20

Python入门指南(五) - 为什么选择 FastAPI? 欢迎来到Python入门指南的第五部分!在上一章中,我们完成了Python开发环境的初始化配置。现在,让我们进入实战阶段——选择合适的Web框架来构建我们的API服务。 本章将深入对比 Flask 和 FastAPI,帮助你理解为什么在现代Python开发中,FastAPI正在成为越来越多开发者的首选。 ** 为什么需要Web框架?** 在进入对比之前,先理解Web框架的核心作用: 处理HTTP请求和响应:接收用户请求,返回处理

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客