共计 2689 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
在前端开发中,我们经常需要与后端 API 进行交互,而大多数 API 都需要在请求头中添加 Token 进行身份验证。手动管理 Token 会带来以下问题:

- 重复代码 :需要在每个请求中手动添加 Authorization 头
- 遗漏风险 :容易忘记添加 Token 导致请求失败
- 刷新复杂 :Token 过期时需要手动刷新并重新发送请求
传统解决方案如直接从 localStorage 读取 Token 或手动添加 Header 都存在明显缺陷:
- 直接在每次请求前从存储中读取 Token 不安全
- 手动添加 Header 会导致代码冗余
- 无法统一处理 Token 刷新逻辑
技术方案
Axios 提供了强大的拦截器机制,可以让我们在请求发送前和响应接收时进行统一处理。核心思路如下:
- 创建独立的 axios 实例
- 配置请求拦截器自动注入 Token
- 配置响应拦截器处理 401 未授权状态
- 实现 Token 刷新和请求重试机制
Axios 拦截器工作原理
Axios 拦截器分为请求拦截器和响应拦截器,它们按照添加顺序形成一个链式调用管道:
- 请求拦截器:从最后一个添加到第一个依次执行
- 响应拦截器:从第一个添加到最后一个依次执行
代码实现
下面是一个完整的 TypeScript 实现方案:
import axios, {AxiosInstance, AxiosRequestConfig, AxiosError} from 'axios';
interface TokenStorage {getToken(): string | null;
setToken(token: string): void;
clearToken(): void;}
class AuthAxios {
private instance: AxiosInstance;
private isRefreshing = false;
private refreshSubscribers: ((token: string) => void)[] = [];
constructor(private tokenStorage: TokenStorage, baseURL: string) {this.instance = axios.create({ baseURL});
this.setupRequestInterceptor();
this.setupResponseInterceptor();}
private setupRequestInterceptor() {
this.instance.interceptors.request.use((config) => {const token = this.tokenStorage.getToken();
if (token) {config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
}
private setupResponseInterceptor() {
this.instance.interceptors.response.use((response) => response,
async (error: AxiosError) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {if (this.isRefreshing) {return new Promise((resolve) => {this.refreshSubscribers.push((token) => {originalRequest.headers.Authorization = `Bearer ${token}`;
resolve(this.instance(originalRequest));
});
});
}
originalRequest._retry = true;
this.isRefreshing = true;
try {const newToken = await this.refreshToken();
this.tokenStorage.setToken(newToken);
originalRequest.headers.Authorization = `Bearer ${newToken}`;
this.refreshSubscribers.forEach(cb => cb(newToken));
this.refreshSubscribers = [];
return this.instance(originalRequest);
} catch (refreshError) {this.tokenStorage.clearToken();
return Promise.reject(refreshError);
} finally {this.isRefreshing = false;}
}
return Promise.reject(error);
}
);
}
private async refreshToken(): Promise<string> {
// 实现你的 Token 刷新逻辑
// 示例:调用刷新 Token 的 API
const response = await axios.post('/auth/refresh');
return response.data.token;
}
public getInstance(): AxiosInstance {return this.instance;}
}
生产环境考量
在实际生产环境中,我们还需要考虑以下方面:
安全建议
- 使用 HttpOnly Cookie 存储敏感 Token
- 实现 CSRF 防护机制
- 避免在拦截器中存储过多状态导致内存泄漏
性能优化
- 减少不必要的 Token 验证请求
- 实现请求缓存机制
- 对于公开 API 跳过 Token 注入
单元测试
测试时应重点覆盖以下场景:
- 正常请求自动添加 Token
- 401 状态码触发 Token 刷新
- 并发请求时的 Token 刷新队列
- 刷新 Token 失败后的处理
避坑指南
在实现过程中可能会遇到以下问题:
- 拦截器执行顺序 :后添加的请求拦截器先执行
- 循环重试 :设置最大重试次数避免死循环
- SSR 场景 :服务端渲染时注意 Cookie 处理
延伸思考
这种模式可以与其他技术栈结合:
- Redux:将 Token 存储在 Redux 中并通过中间件注入
- SWR:配合 SWR 的全局配置实现类似功能
- GraphQL:Apollo Client 提供了类似的认证拦截机制
通过这种集中式管理 Token 的方式,我们的代码会更加简洁健壮,同时也更容易维护和扩展。希望这篇指南能帮助你更好地理解和使用 Axios 的拦截器机制。
正文完
