96SEO 2026-05-07 17:37 1
基于 Token的身份认证Yi成为标配。然而Token 的时效性是一把双刃剑:为了安全,它必须短命;但为了体验,它又需要长久。如何化解这对矛盾?答案就在于——Axios Token 无感刷新机制。

今天我们就来深入探讨一下如何利用 Axios 的拦截器特性,构建一套健壮、丝滑且安全的 Token 自动刷新方案。这不仅仅是一段代码的堆砌,geng是一场关于用户体验与安全性的深度博弈。
一、 为什么我们需要“无感刷新”?想象一下这样的场景:你正在填写一份长达数页的复杂表单,手指在键盘上飞舞,终于点击了“提交”按钮。屏幕转圈,然后——啪!一个红色的 401 错误弹窗横亘在你眼前,告诉你 Token 失效了。当你重新登录回来发现刚才填写的所有数据dou化为乌有。此时此刻,用户的内心大概是崩溃的,甚至可Neng直接卸载应用。
传统的单 Token 模式存在天然的缺陷。Ru果 Token 设置得过长,一旦被黑客截获,在有效期内他们Ke以为所欲为,安全隐患巨大;Ru果设置得过短,用户就需要频繁登录,体验极差。
为了解决这个问题,业界普遍采用了双 Token 机制
Access Token: 身份的临时通行证。有效期极短,用于访问业务接口。一旦泄露,损失可控。
Refresh Token: 身份的长期凭证。有效期较长,用于在 Access Token 过期时向后端申请一个新的 Access Token。它通常只存在于刷新接口的交互中,不参与业务逻辑。
所谓的“无感刷新”,就是当 Access Token 过期时前端静默地利用 Refresh Token 获取新的令牌,并重试刚才失败的请求。整个过程对用户透明,就像什么dou没发生过一样。
二、 核心架构:Axios 拦截器的艺术Axios 之所以成为前端 HTTP 客户端的首选,其强大的拦截器功Neng功不可没。拦截器就像是在请求和响应的必经之路上设立的关卡,我们Ke以在那里检查证件、修改数据、甚至拦截行程。
实现无感刷新,我们需要在两个关卡上下功夫:
请求拦截器: 负责在每次发请求前,自动把Zui新的 Access Token 塞进请求头的 Authorization 字段里。
响应拦截器: 负责监听服务端的反馈。一旦捕捉到 401 Unauthorized 的信号,立即启动刷新流程,并暂停后续处理。
1. 基础铺垫:创建实例与请求注入我们不要直接使用全局的 axios 对象,而是创建一个独立的实例。这样Ke以避免污染全局配置,也便于隔离不同业务模块的请求逻辑。
import axios from 'axios';
// 创建一个专属的 Axios 实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 你的 API 基础路径
timeout: 10000, // 请求超时时间
});
// 请求拦截器:自动携带 Token
service.interceptors.request.use(
config => {
// 从本地存储中获取 Access Token
const accessToken = localStorage.getItem;
if {
// 确保 Authorization 头存在并采用 Bearer 模式
config.headers = `Bearer ${accessToken}`;
}
return config;
},
error => {
// 对请求错误Zuo些什么
return Promise.reject;
}
);
这一步hen简单,就像出门前检查口袋里有没有带钥匙。有了这层保障,所有的业务请求dou会自动带上身份信息。
三、 难点攻克:响应拦截器与并发控制真正的挑战在于响应拦截器。这里不仅要处理 Token 过期,还要解决一个极其棘手的问题:并发请求竞态。
试想一下Ru果页面初始化时同时发出了 5 个请求,而此时 Access Token 恰好过期。这 5 个请求几乎同时返回 401 错误。Ru果没有控制机制,前端会连续发起 5 次刷新 Token 的请求。这不仅浪费服务器资源,还可Neng导致逻辑混乱。
为了解决这个问题,我们需要引入两个核心变量:
isRefreshing一个布尔值锁。标记当前是否正在进行刷新操作。
failedQueue一个数组队列。用于存储在刷新期间等待重试的请求。
下面是经过千锤百炼的生产级代码。请注意,这里我们采用了“失败重试”的策略,即先发请求,遇到 401 再刷新。这种方式比“预判过期”geng可靠,因为它不依赖客户端的时间,完全由服务端掌控。
// 全局变量:状态锁与请求队列
let isRefreshing = false;
let failedQueue = ;
// 辅助函数:处理队列中的请求
const processQueue = => {
failedQueue.forEach(prom => {
if {
prom.reject;
} else {
prom.resolve;
}
});
failedQueue = ;
};
// 响应拦截器
service.interceptors.response.use(
response => {
// Ru果响应状态码在 2xx 范围内,直接返回数据
return response.data;
},
async error => {
const { config, response } = error;
// Ru果没有配置对象或者没有响应对象,直接报错
if return Promise.reject;
// 判断是否为 401 错误
if {
// 特殊情况:Ru果当前请求本身就是刷新 Token 的接口,并且返回了 401
// 这说明 Refresh Token 也过期了必须强制跳转登录页,否则会陷入死循环
if {
localStorage.removeItem;
localStorage.removeItem;
window.location.href = '/login';
return Promise.reject;
}
// 情况 A:当前没有正在进行的刷新任务
// 我们需要由当前这个请求来触发刷新,并锁住状态
if {
isRefreshing = true;
try {
// 获取本地的 Refresh Token
const refreshToken = localStorage.getItem;
// 发起刷新请求
const refreshRes = await axios.post('/auth/refresh', {
refresh_token: refreshToken
});
const newAccessToken = refreshRes.data.access_token;
const newRefreshToken = refreshRes.data.refresh_token;
// geng新本地存储
localStorage.setItem;
localStorage.setItem;
// geng新当前请求的配置头,以便后续重试
config.headers.Authorization = `Bearer ${newAccessToken}`;
// 刷新成功!解锁状态
isRefreshing = false;
// 1. 立即重试当前失败的请求
// 2. 通知队列中所有等待的请求,让它们也用新 Token 重试
processQueue;
return service; // 重新发起原请求
} catch {
// 刷新失败
isRefreshing = false;
processQueue;
// 清理本地数据并跳转登录
localStorage.removeItem;
localStorage.removeItem;
window.location.href = '/login';
return Promise.reject;
}
}
// 情况 B:Yi经有请求在刷新中了
// 当前请求不需要自己刷新,只需加入队列,等待结果
else {
return new Promise => {
failedQueue.push({
resolve: => {
// 拿到新 Token 后geng新请求头并重试
config.headers.Authorization = `Bearer ${token}`;
resolve);
},
reject: => {
reject;
}
});
});
}
}
// 非 401 错误,直接抛出
return Promise.reject;
}
);
这段代码的逻辑其实非常精妙。它就像一个交通指挥官:
当第一个 401 到来时它放下栏杆,禁止后续车辆通过并派出一辆车去修路。
后续到来的 401 车辆被引导到旁边的等待区。
路修好后指挥官升起栏杆,给等待区的所有车辆发了一张新的通行证,让它们依次通过。
Ru果路没修好,指挥官就告诉大家此路不通,全部遣返回家。
四、 安全性:不仅仅是代码逻辑实现了功Neng只是第一步,安全性才是重中之重。关于 Token 刷新,有几个不得不提的安全细节。
1. Refresh Token 轮换机制hen多开发者容易忽略的一点是:Refresh Token 本身也是需要“新陈代谢”的。每次使用 Refresh Token 去换取新的 Access Token 时服务端应该同时下发一个新的 Refresh Token,并立即将旧的 Refresh Token 作废。
这就像酒店换房卡一样。你用旧房卡去前台续期,前台不仅给你新房卡,还会把旧房卡注销。这样,即使黑客窃取了你旧的 Refresh Token,当你下次正常刷新时黑客手里的那张旧卡也就失效了。这Neng有效防范重放攻击。
2. 存储策略的抉择Access Token 和 Refresh Token 存在哪里?localStorage?sessionStorage?还是 Cookie?
这是一个经典的权衡问题:
LocalStorage: 方便,但容易受到 XSS的威胁。Ru果页面被注入了恶意脚本,Token 就会被盗走。
HttpOnly Cookie: 无法通过 JavaScript 读取,免疫 XSS 攻击。这是存储 Refresh Token 的Zui佳位置。配合 HTTPS,还Neng防止 CSRF 攻击。
目前的Zui佳实践通常是:Access Token 存在内存或 LocalStorage 中,Refresh Token 存在 HttpOnly Cookie 中。这样即使前端被攻破,黑客也拿不到用于续命的 Refresh Token。
五、 避坑指南:那些年我们踩过的坑在实现这套机制的过程中,有一些细节非常容易让人“翻车”。这里分享几个经验之谈。
1. 刷新接口的独立性在代码示例中,你可Neng注意到了我建议使用独立的 axios 实例或者直接在拦截器中判断 URL。这是为了防止递归死循环。
Ru果刷新 Token 的请求也经过了业务层的响应拦截器,而它恰好也返回了 401,那么拦截器会 尝试刷新,从而陷入无限递归调用,直到浏览器崩溃。因此,一定要在拦截器中排除刷新接口本身,或者为刷新接口创建一个不带拦截逻辑的纯净 Axios 实例。
2. 跨标签页同步现代用户习惯同时打开多个标签页。Ru果用户在标签页 A 中操作触发了刷新,获取了新 Token,但标签页 B 还在使用旧 Token,那么标签页 B 的请求就会全部失败。
为了解决这个问题,Ke以利用 storage 事件。当 LocalStorage 发生变化时其他标签页Neng监听到这个事件,从而同步geng新自己内存中的 Token。
// 在标签页中监听 storage 事件
window.addEventListener => {
if {
// 检测到 Token 变化,geng新内存或重新设置请求头
// 这里Ke以触发一个自定义事件,通知应用geng新状态
}
});
Axios Token 刷新机制的实现,kan似只是几行代码的拼接,实则是对 HTTP 协议、异步编程以及安全策略的综合运用。通过双 Token 机制,我们巧妙地在安全与体验之间找到了平衡点。
当你在代码中写下那个 isRefreshing 锁,kan着控制台里原本红色的 401 错误被自动重试的绿色 200 所取代,那种流畅感不仅属于用户,也属于每一个追求极致体验的开发者。希望这篇文章Neng帮你彻底搞定 Token 刷新的难题,让你的应用在用户眼中永远“在线”。毕竟Zui好的技术,就是让用户感觉不到技术的存在。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback