在 javascript 中实现接口请求的令牌策略,核心做法是在 fetch 发起请求前拦截并注入 token,并在收到 401 unauthorized 响应时触发刷新逻辑。具体而言,需要封装一个自定义函数或类,维护当前有效的访问令牌(access token)和刷新令牌(refresh token),利用 promise 链处理并发请求时的重复刷新问题。当检测到令牌过期时,程序会暂停后续请求,调用刷新接口获取新令牌,更新本地存储后重试失败的请求,从而保证业务逻辑的连续性。

如何封装基础请求以自动携带 Token
要实现自动携带 Token,最直接的方式是创建一个包装函数,在每次调用 fetch 之前检查内存中的令牌状态并将其添加到 headers 中。这种做法适用于大多数常规 API 调用,开发者只需替换原有的原生 fetch 调用即可。关键在于处理令牌为空的情况,通常会在首次加载时尝试从本地存储恢复,若不存在则引导用户重新登录。通过这种方式,所有网络请求都隐式地拥有了认证能力,无需在每个业务模块中重复编写鉴权代码。

const TOKEN_KEY = 'access_token';
function getToken() {
return localStorage.getItem(TOKEN_KEY);
}
function setToken(token) {
localStorage.setItem(TOKEN_KEY, token);
}
async function request(url, options = {}) {
const token = getToken();
const headers = {
...options.headers,
'Authorization': token ? `Bearer ${token}` : ''
};
try {
const response = await fetch(url, { ...options, headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
上述代码定义了一个简单的 request 函数。它首先通过 getToken 获取存储在 localStorage 中的令牌。如果存在令牌,则将其格式化为 Bearer 形式放入 Authorization 头部。随后调用原生 fetch 发送请求。如果响应状态码不在 2xx 范围内,则抛出错误。这种基础封装确保了每个请求都附带了必要的身份凭证,是实现令牌策略的第一步。
如何处理 401 响应并刷新令牌
当服务器返回 401 状态码时,意味着当前 Access Token 已失效。此时不应直接报错给用户,而应启动刷新流程。我们需要捕获该错误,调用专门的刷新接口获取新的 Access Token,更新本地存储,然后重试原始请求。为了避免多个并发请求同时触发刷新导致资源浪费或状态冲突,必须引入一个“刷新中”的状态锁。只有第一个遇到 401 的请求负责执行刷新操作,其他请求则等待刷新完成后再统一重试。

let isRefreshing = false;
let requests = [];
async function refreshToken() {
const refreshKey = 'refresh_token';
const refreshTok = localStorage.getItem(refreshKey);
if (!refreshTok) throw new Error('No refresh token');
const res = await fetch('/api/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: refreshTok })
});
if (!res.ok) throw new Error('Refresh failed');
const data = await res.json();
setToken(data.access_token);
return data.access_token;
}
async function requestWithAuth(url, options = {}) {
try {
return await request(url, options);
} catch (error) {
if (error.message.includes('401')) {
if (!isRefreshing) {
isRefreshing = true;
try {
await refreshToken();
isRefreshing = false;
// 重试队列中的所有请求
requests.forEach(cb => cb());
requests = [];
// 重试当前请求
return request(url, options);
} catch (refreshErr) {
isRefreshing = false;
requests.forEach(cb => cb());
requests = [];
window.location.href = '/login';
throw refreshErr;
}
} else {
// 正在刷新,将当前请求加入队列
return new Promise((resolve) => {
requests.push(() => resolve(request(url, options)));
});
}
}
throw error;
}
}
这段代码展示了完整的令牌刷新机制。isRefreshing 标志位用于防止并发刷新。requests 数组用于暂存因 401 而挂起的请求回调。当捕获到 401 错误时,如果当前没有在刷新,则设置标志位为真并调用 refreshToken。刷新成功后,遍历队列执行所有挂起的请求,并重置标志位。如果刷新失败,则清空队列并跳转至登录页。如果当前已在刷新中,则将当前请求包装成 Promise 推入队列,等待后续执行。这种模式有效解决了令牌过期时的用户体验问题。
基于并发请求队列的具体行为验证
在实际应用中,页面初始化时可能会同时发起多个 API 请求。如果此时 Access Token 恰好过期,这些请求都会收到 401 响应。根据上述实现,只有第一个到达的触发刷新逻辑,其余请求会被阻塞并加入 requests 队列。一旦 refreshToken 成功返回新令牌,队列中的每一个回调都会被立即执行,使用新的令牌重新发起请求。这意味着用户不会看到任何中间状态的错误提示,所有数据最终都能正确加载。这种基于队列的重试机制是令牌策略中处理高并发场景的关键。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











