Vue + Spring Security项目里,我是这样用Axios拦截器优雅刷新JWT Token的

发布时间:2026/6/10 5:43:01

Vue + Spring Security项目里,我是这样用Axios拦截器优雅刷新JWT Token的 Vue Spring Security项目中Axios拦截器实现JWT Token无感刷新实战最近在重构公司内部管理系统时遇到了一个典型场景用户正在填写复杂表单时突然弹出登录失效提示。这种糟糕的体验让我开始深入研究如何在前端实现Token的无感刷新。本文将分享我在Vue项目中结合Axios拦截器实现的完整方案包含请求队列管理、防并发刷新等实战技巧。1. JWT双Token机制设计原理现代Web应用普遍采用JWT作为身份验证方案但其固定有效期的特性带来了用户体验挑战。我们采用的解决方案是双Token机制Access Token短期有效通常2小时用于常规API请求授权Refresh Token长期有效通常7天专门用于获取新的Access Token这种设计的安全优势在于即使Access Token被截获攻击者也只能在短时间内滥用Refresh Token有独立生命周期可单独设置更严格的安全策略后端实现的核心逻辑如下表所示场景后端响应前端处理Access Token有效正常返回数据继续业务流程Access Token过期但Refresh Token有效返回401状态码使用Refresh Token获取新Access Token双Token均过期返回401状态码跳转至登录页2. Axios拦截器核心实现2.1 基础拦截器配置首先创建Axios实例并配置基础拦截器const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) // 请求拦截器 service.interceptors.request.use(config { const token localStorage.getItem(access_token) if (token) { config.headers.Authorization Bearer ${token} } return config }, error { return Promise.reject(error) })2.2 响应拦截器中的Token刷新逻辑关键部分在于响应拦截器中处理401错误的逻辑let isRefreshing false let refreshSubscribers [] // 响应拦截器 service.interceptors.response.use( response response, async error { const originalRequest error.config if (error.response.status 401 !originalRequest._retry) { if (isRefreshing) { return new Promise(resolve { refreshSubscribers.push(token { originalRequest.headers.Authorization Bearer ${token} resolve(service(originalRequest)) }) }) } originalRequest._retry true isRefreshing true try { const newTokens await refreshToken() localStorage.setItem(access_token, newTokens.access_token) localStorage.setItem(refresh_token, newTokens.refresh_token) refreshSubscribers.forEach(cb cb(newTokens.access_token)) refreshSubscribers [] return service(originalRequest) } catch (refreshError) { router.push(/login) return Promise.reject(refreshError) } finally { isRefreshing false } } return Promise.reject(error) } )这段代码实现了几个关键功能防并发刷新通过isRefreshing标志位确保同一时间只有一个刷新请求请求队列在刷新过程中将后续请求暂存待新Token获取后重试错误隔离刷新失败时直接跳转登录不影响其他错误处理3. 高级优化技巧3.1 Token自动续期策略为避免用户在非活跃状态下Token过期可以设置定时刷新function setupTokenRefresh() { const refreshInterval 30 * 60 * 1000 // 每30分钟检查一次 setInterval(async () { const tokenExp getTokenExpiration() const now Math.floor(Date.now() / 1000) if (tokenExp - now 1800) { // 剩余30分钟内过期 try { const newTokens await silentRefresh() storeTokens(newTokens) } catch (error) { console.error(后台刷新Token失败, error) } } }, refreshInterval) }3.2 多Tab同步方案当用户在多个浏览器Tab中操作时需要确保Token状态同步window.addEventListener(storage, (event) { if (event.key token_update) { const newToken JSON.parse(event.newValue) service.defaults.headers.common[Authorization] Bearer ${newToken} } }) // 在成功刷新Token后触发事件 function broadcastNewToken(token) { localStorage.setItem(token_update, JSON.stringify(token)) }4. 安全增强措施4.1 Refresh Token保护策略为增强Refresh Token安全性建议实施以下措施HttpOnly Cookie存储防止XSS攻击获取Refresh Token使用次数限制记录Refresh Token使用次数异常时立即失效IP绑定将Refresh Token与首次使用时IP绑定4.2 敏感操作二次验证对于关键操作如修改密码即使Token有效也应要求重新认证function performSensitiveAction() { if (!recentlyAuthenticated()) { showAuthModal() .then(() { // 用户通过验证后继续操作 actualSensitiveAction() }) } else { actualSensitiveAction() } }5. 性能与异常处理5.1 请求超时优化针对刷新Token场景设置独立超时const refreshService axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 // 比常规请求更短的超时 })5.2 错误降级方案当刷新机制失败时提供优雅降级方案本地缓存将未提交数据自动保存至localStorage错误恢复在登录后提供恢复草稿的选项心跳检测定期检查Token状态提前预警function saveDraft(data) { localStorage.setItem(form_draft, JSON.stringify({ data, savedAt: new Date().toISOString() })) } function checkForDraft() { const draft localStorage.getItem(form_draft) if (draft) { showRestorePrompt(JSON.parse(draft)) } }在实际项目中实施这套方案后用户会话中断的投诉减少了约80%。特别是在数据看板这类需要长时间保持页面打开的场景下用户体验得到了显著提升。

相关新闻