Nuxt 3中使用useFetch处理401错误、刷新Token并重发请求的最佳方式
在Nuxt 3中用useFetch处理401并自动刷新Token的最佳实践
核心思路
通过封装带认证逻辑的自定义useFetch,拦截401未授权错误,触发Token刷新流程,并重发原请求。同时通过锁机制避免重复刷新,用请求队列处理等待中的并发请求。
实现步骤与代码示例
1. 封装自定义useAuthFetch
这个组合式函数会自动处理Token注入、401拦截、刷新并重发请求:
import { useFetch, useCookie, navigateTo } from '#app' // 存储Token的Cookie(适配SSR场景) const accessToken = useCookie('access_token') const refreshToken = useCookie('refresh_token') // 防止并发刷新的锁 let isRefreshing = false // 存储等待重发的请求队列 let failedRequestsQueue = [] export const useAuthFetch = (url, options = {}) => { // 给请求头注入当前AccessToken const requestHeaders = { ...options.headers, ...(accessToken.value && { Authorization: `Bearer ${accessToken.value}` }) } return useFetch(url, { ...options, headers: requestHeaders, async onError({ error, retry, options }) { // 仅处理401未授权错误 if (error.response?.status !== 401) return if (isRefreshing) { // 若正在刷新Token,将当前请求加入队列等待 return new Promise(resolve => { failedRequestsQueue.push(() => resolve(retry())) }) } isRefreshing = true try { // 调用刷新Token接口 const { data: newTokens } = await useFetch('/api/auth/refresh', { method: 'POST', body: { refresh_token: refreshToken.value } }) if (!newTokens.value) throw new Error('Token刷新失败') // 更新本地Token accessToken.value = newTokens.value.access_token refreshToken.value = newTokens.value.refresh_token // 更新当前请求的Authorization头 options.headers.Authorization = `Bearer ${newTokens.value.access_token}` // 重试当前请求 await retry() // 批量重发队列中等待的请求 failedRequestsQueue.forEach(callback => callback()) failedRequestsQueue = [] } catch (refreshError) { // 刷新失败,清理Token并跳转登录页 accessToken.value = null refreshToken.value = null navigateTo('/login') } finally { isRefreshing = false } } }) }
2. 在业务代码中使用
直接用useAuthFetch替代原生useFetch即可,无需额外处理认证逻辑:
// 页面/组件中获取用户信息 const { data: userProfile, error } = useAuthFetch('/api/user/profile')
关键注意事项
- 刷新接口的特殊性:确保
/api/auth/refresh接口本身不受401拦截(需允许通过RefreshToken访问) - 并发控制:
isRefreshing锁避免多个401请求同时触发Token刷新,减少无效接口调用 - 队列处理:保证Token刷新后,所有之前因401失败的请求都能重新发起,不丢失用户操作
- 失败降级:刷新Token失败时,必须清理本地Token并跳转登录,避免陷入401死循环
内容的提问来源于stack exchange,提问作者Jakub Rosina
相关产品推荐
相关产品推荐

