共计 2376 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点分析
在现代前端应用中,JWT Token 作为身份验证的常见方式,通常需要通过请求头传递给后端。但这种做法存在两个主要问题:

- 安全风险 :Token 直接暴露在请求头中,容易被中间人攻击或通过 XSS 漏洞窃取
- 并发问题 :当 Token 过期需要刷新时,多个并发请求可能同时触发刷新逻辑,导致 Token 竞争
技术方案对比
1. 手动添加 headers
// 每次请求都需要重复写 headers
axios.get('/api/data', {headers: { Authorization: `Bearer ${token}` }
})
- 优点:简单直接
- 缺点:代码重复,容易遗漏,维护困难
2. axios 拦截器统一注入
// 请求拦截器示例
axios.interceptors.request.use(config => {config.headers.Authorization = `Bearer ${getToken()}`
return config
})
- 优点:集中管理,避免重复代码
- 缺点:需要处理 Token 刷新等复杂场景
3. 结合状态管理
// 结合 Redux/Vuex 的示例
store.subscribe(() => {const token = store.getState().auth.token
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
})
- 优点:与应用状态深度集成
- 缺点:增加了架构复杂度
核心代码实现
完整的 axios 拦截器配置
import axios from 'axios'
import {refreshToken} from './auth'
let isRefreshing = false
let failedQueue: any[] = []
const processQueue = (error: any, token?: string) => {
failedQueue.forEach(prom => {if (error) {prom.reject(error)
} else {prom.resolve(token)
}
})
failedQueue = []}
axios.interceptors.request.use(config => {const token = localStorage.getItem('token')
if (token) {config.headers.Authorization = `Bearer ${token}`
}
return config
}, error => {return Promise.reject(error)
})
axios.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {if (isRefreshing) {return new Promise((resolve, reject) => {failedQueue.push({ resolve, reject})
}).then(token => {originalRequest.headers.Authorization = `Bearer ${token}`
return axios(originalRequest)
}).catch(err => {return Promise.reject(err)
})
}
originalRequest._retry = true
isRefreshing = true
try {const newToken = await refreshToken()
localStorage.setItem('token', newToken)
axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`
processQueue(null, newToken)
return axios(originalRequest)
} catch (err) {processQueue(err)
return Promise.reject(err)
} finally {isRefreshing = false}
}
return Promise.reject(error)
}
)
性能优化
- 减少 Token 验证请求 :
- 对静态资源请求禁用拦截器
-
实现 Token 有效期缓存检查
-
请求队列管理 :
- 如上述代码所示,使用队列处理并发刷新场景
- 设置合理的超时时间(建议 5 -10 秒)
安全考量
HttpOnly Cookie vs Header
| 方案 | 优点 | 缺点 |
|---|---|---|
| Header | 适合前后端分离架构 | 易受 XSS 攻击 |
| HttpOnly Cookie | 防止 XSS 窃取 | 需处理 CSRF 防护 |
CSRF 防护方案
// 双重提交 Cookie 模式
axios.interceptors.request.use(config => {const csrfToken = getCSRFToken() // 从 meta 标签或 Cookie 获取
config.headers['X-CSRF-TOKEN'] = csrfToken
return config
})
生产环境避坑指南
- 避免 localStorage 存储风险 :
- 考虑使用 sessionStorage
-
或实现内存存储 + 定时刷新
-
SSR 场景处理 :
- 服务端请求使用单独的 axios 实例
- 通过 Cookie 传递 Token
总结与思考
通过 axios 拦截器管理 Token 是一个平衡安全性和开发效率的解决方案。但在实际项目中,我们还需要考虑:
- 如何实现无感刷新 Token?
- 在微前端架构下如何共享认证状态?
- 是否可以考虑使用 OAuth 2.0 的静默刷新机制?
这些问题的答案可能因项目架构而异,但核心原则始终是:在保证安全的前提下,提供最佳的用户体验。
正文完
