JavaScript中实现凭证无感刷新的核心是拦截401响应、用refresh token换取新access token并重放请求;需服务端支持/auth/refresh接口,前端用Axios拦截器+刷新队列避免并发重复刷新,token应内存存储access token、httpOnly Cookie存储refresh token以保障安全。

在 JavaScript 中实现凭证过期后的“无感刷新”,核心是拦截 401 响应、自动用刷新令牌(refresh token)换取新访问令牌(access token),并重放原请求,整个过程对业务代码透明。关键不在“完全无感”,而在于避免用户手动登录或页面跳转。
前置条件:服务端需支持刷新机制
客户端无法凭空续期凭证,必须依赖服务端提供的刷新能力:
- 登录成功后,服务端返回 access_token(短期有效,如 15–30 分钟)和 refresh_token(长期有效,带存储限制与绑定,如 IP/UA/设备指纹)
- 提供独立的 /auth/refresh 接口,接收 refresh_token,校验通过后返回新的 access_token(可选新 refresh_token)
- 该刷新接口本身不校验 access_token,且应允许跨域(若前端分离部署)
使用 Axios 实现请求拦截 + 刷新队列
推荐用 Axios,因其拦截器和 CancelToken(或 AbortController)便于控制并发刷新请求。重点解决“多个请求几乎同时 401”时只发起一次刷新的问题:
- 维护一个 refreshingPromise:当首次 401 触发刷新时创建 Promise 并缓存;后续 401 直接 await 它,复用结果
- 维护一个 failedQueue 数组:暂存因 401 被挂起的原始请求配置(含 resolve/reject 回调)
- 刷新成功后,遍历队列,用新 token 重发所有请求;失败则清空队列并 reject 所有请求
示例关键逻辑(简化版):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
let isRefreshing = false;
let failedQueue = [];
axios.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (!isRefreshing) {
isRefreshing = true;
try {
const { data } = await axios.post('/auth/refresh', {
refresh_token: getRefreshToken()
});
setAccessToken(data.access_token);
// 清空队列,重试所有请求
failedQueue.forEach(({ config, resolve }) => {
config.headers.Authorization = `Bearer ${data.access_token}`;
resolve(axios(config));
});
failedQueue = [];
} catch (refreshErr) {
// 刷新失败 → 清除凭证,跳登录页
clearAuth();
window.location.href = '/login';
} finally {
isRefreshing = false;
}
}
return new Promise((resolve, reject) => {
failedQueue.push({ config: originalRequest, resolve, reject });
});
}
return Promise.reject(error);
}
);
注意 Token 存储与安全边界
前端无法彻底规避凭证泄露风险,但可降低影响:
- access_token:仅存内存(如闭包变量或 Vuex/Pinia state),不存 localStorage/sessionStorage(防 XSS)
- refresh_token:可存 httpOnly + Secure + SameSite=Strict 的 Cookie(服务端下发),前端 JS 无法读取,更安全;若必须前端存储(如部分 OAuth 流程),至少用 sessionStorage 且设置短过期
- 每次刷新成功后,更新内存中的 access_token,并确保后续请求拦截器自动注入 Authorization 头
补充体验细节:避免白屏与重复提交
无感 ≠ 无感知,需配合 UI 层减少用户困惑:
- 全局请求拦截器中,对被挂起的请求,可标记
originalRequest._retry = true防止无限循环 - 刷新期间,若用户触发新请求(非队列中),可显示轻量 loading(如顶部进度条),而非阻塞整个页面
- 表单提交类请求,建议加防抖或提交后禁用按钮,防止用户连续点击导致多个 401 请求堆积
- 若刷新耗时较长(如网络差),可加超时控制(如 10s 后 fallback 到登录页)

















