在前后端分离的项目中,为了安全,Token 通常会设置有效期。但如果 Token 过期时强制用户重新登录,会极大地破坏用户体验。如何做到在用户毫无察觉的情况下,自动完成 Token 的续期?本文将深度拆解 "双 Token 无感刷新" 的实现机制。
举个简单例子,你正在某 App 编辑内容,中途切出几分钟,再切回来时,直接弹出登录页,提示"登录已过期,请重新登录",这种场景很容易让用户流失。
传统的单 Token 方案存在一个两难境地:
- 有效期过短:用户操作频繁,动不动就跳回登录页,用户体验极差。
- 有效期过长:Token 一旦被截获,风险极高。
解决方案:双 Token 机制
- access_token:访问令牌。有效期短(如 1 小时),每次接口请求都携带,降低泄露风险。
- refresh_token :刷新令牌。有效期长(如 7 天),仅用于
access_token过期时换取新令牌。
只要用户在 7 天内活跃过,系统就能通过 refresh_token 自动"续命",实现长效无感登录。
- 正常请求 :前端携带
access_token访问。 - 触发过期 :后端返回 401 Unauthorized。
- 判断逻辑:
- 如果是普通接口报 401:说明
access_token失效,尝试刷新。 - 如果是刷新接口 报 401:说明
refresh_token也失效了,强制重新登录。
- 如果是普通接口报 401:说明
- 无感替换 :前端自动调用刷新接口,获取新 Token 覆盖本地存储,并重新发起之前失败的请求。
痛点:如果页面同时发出了 5 个请求,而此时 Token 刚好过期,会导致这 5 个请求同时触发"刷新 Token"的操作,造成资源浪费甚至后端异常。
解决策略:
- 状态锁 (
refreshing) :记录当前是否正在刷新中。 - 任务队列 (
queue) :在刷新期间到达的请求,先暂存起来,不直接报错。 - 批量回放:等待 Token 刷新成功后,依次执行队列里的请求,实现"无感"衔接。
以下是基于 Axios 的完整工程化实现:
GPT plus 代充 只需 145import axios, { AxiosRequestConfig } from 'axios';
interface PendingTask {
config: AxiosRequestConfig; resolve: Function;
}
let refreshing = false; // 状态锁:标志是否正在刷新 Token let queue: PendingTask[] = []; // 请求队列:暂存 Token 刷新期间的请求
const axiosInstance = axios.create({
GPT plus 代充 只需 145baseURL: '/api'
});
// 1. 请求拦截器:自动注入 Token axiosInstance.interceptors.request.use((config) => `;
} return config;
});
// 2. 响应拦截器:处理 Token 过期 axiosInstance.interceptors.response.use(
GPT plus 代充 只需 145(response) => response, async (error) => { const { data, config } = error.response; // 情况 A:正在刷新 Token 中,将后续请求存入队列 if (refreshing) { return new Promise((resolve) => { queue.push({ config, resolve }); }); } // 情况 B:access_token 过期 (状态码 401 且非刷新接口本身) if (data.statusCode === 401 && !config.url.includes('/refresh')) ) => { resolve(axiosInstance(config)); }); queue = []; // 清空队列 // 执行当前触发刷新的那个请求 return axiosInstance(config); } } catch (err) { refreshing = false; queue = []; // 情况 C:refresh_token 也过期了,彻底清除登录态 localStorage.clear(); window.location.href = '/login'; return Promise.reject(err); } } return Promise.reject(error); }
);
/
- 刷新 Token 的异步方法 */ async function refreshToken() }); // 更新本地存储 localStorage.setItem(‘access_token’, res.data.accessToken); localStorage.setItem(‘refresh_token’, res.data.refreshToken); return res; }
- 并发请求的 Promise 挂起 :在
refreshing为true时,返回一个不带resolve的new Promise是关键,它能让 Axios 请求处于pending状态。 - 错误捕获 :
refreshToken接口本身报错(如 500 或 401)必须妥善处理,直接引导至登录页。 - 安全性 :普通项目中可以使用
localStorage,但在更高要求的项目中,建议配合HttpOnly Cookie存储refresh_token以防 XSS 攻击。 - 接口重定向陷阱:确保刷新 Token 的接口不会再次进入 401 拦截死循环。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/243176.html