使用Promise刷新JWT更新localStorage后axios请求获取旧header问题
问题原因
authHeader是同步执行函数,检测到token过期时触发的refreshToken()是异步操作,不会阻塞当前函数返回。你在refreshToken().then()回调中返回的新请求头只会作为Promise的结果,不会被外层authHeader接收,函数会直接执行后续的return语句,返回旧token构造的请求头。- axios的
headers配置项默认只接收普通对象,不会自动等待异步Promise的执行结果,即便你修改authHeader返回Promise,axios也不会等Promise完成再发起请求。
另外你代码中jwt_decode(icpToken)属于笔误,参数应该是你前面定义的token变量。
解决方案
方案1:改造现有逻辑,将token校验流程改为异步
首先将authHeader改为异步函数,等待token刷新完成再返回请求头:
async authHeader() { let token = loadToken()?.access_token // loadToken负责从localStorage加载token if (!token) { return {}; } const epoch = new Date().getTime() / 1000 const decodedJwt = jwt_decode(token) if (decodedJwt.exp <= epoch) { // 等待token刷新完成再继续 await this.refreshToken() } return { "Authorization": 'Bearer ' + loadToken()?.access_token } }
然后改造getProfile,先异步获取请求头再发起请求:
async getProfile(success, error) { // 等待请求头构造完成,确保token是最新的 const headers = await this.authHeader() return axios .get("/auth/me/", { headers }) .then(resp => { if (resp.status === 200) { success(resp.data) } // ...其余逻辑 }) }
方案2:使用axios请求拦截器统一处理(更推荐)
全局添加请求拦截器,统一处理所有请求的token校验和刷新逻辑,无需每个请求单独处理:
// 全局注册axios请求拦截器 axios.interceptors.request.use( async (config) => { let token = loadToken()?.access_token if (token) { const epoch = new Date().getTime() / 1000 const decodedJwt = jwt_decode(token) // token过期先执行刷新 if (decodedJwt.exp <= epoch) { await refreshToken() token = loadToken()?.access_token } // 给当前请求自动添加授权头 config.headers.Authorization = `Bearer ${token}` } return config }, (error) => Promise.reject(error) ) // 改造后getProfile无需手动传headers getProfile(success, error) { return axios .get("/auth/me/") .then(resp => { if (resp.status === 200) { success(resp.data) } // ...其余逻辑 }) }
补充优化建议
可以给refreshToken添加错误捕获逻辑,如果刷新token失败(比如refresh_token也过期),直接清空本地存储跳转到登录页,避免进入死循环。
内容的提问来源于stack exchange,提问作者WebDev1
相关产品推荐
相关产品推荐

