You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

刷新Token后请求结果无法返回至Pinia Store的问题求助

问题描述

通过Pinia Store使用Axios发起定时请求,存在多请求并发场景。服务端采用需刷新的Token机制,已编写拦截器在Access Token过期前调用Refresh Token,并将Token刷新期间的请求存入数组。目前Token刷新功能正常,但刷新后的请求结果无法返回至发起请求的Pinia Store,导致数据未更新。

用户提供的代码:

import axios from 'axios';
import {useAuthStore} from "@/stores/Authentication/AppAuthStore.js";
import VueJwtDecode from 'vue-jwt-decode';


axios.defaults.baseURL = import.meta.env.VITE_API_ENDPOINT;
axios.defaults.maxRedirects = 0;
let refreshing_token = null;
let myTimeout= null;
let busy = false;
let requestArray = [];
const baseURL = import.meta.env.VITE_API_ENDPOINT;


axios.interceptors.request.use( async req =>{
    if (req.url.endsWith("refresh")) {
        // if we are making a refresh token call, return it to prevent infinite loop
        return req;
    }

    const accesstoken = sessionStorage.getItem("accesstoken")
    if (accesstoken){
        req.headers.Authorization = `Bearer ${accesstoken}`;
        //const {header,payload} = useJwt(accesstoken)
        let user = VueJwtDecode.decode(accesstoken);
        const isExpired = Date.now() >= ((user.exp * 1000) - 10000)
        console.log(isExpired)
        if (!isExpired) return req;
        requestArray.push(req);
        console.log("is expired");
/*        refreshing_token = refreshing_token ? refreshing_token : refresh_token();
        let res  = await refreshing_token;
        refreshing_token = null;
        clearTimeout(myTimeout);*/
        if (!busy){
            busy = true;
            const refreshtoken = sessionStorage.getItem("refreshtoken")
            axios.post(baseURL + 'auth/refresh', {
                refresh_token : refreshtoken
            }).then((res) => {
                console.log(res);
                console.log("token refreshed : " + res.data.accesstoken);
                const authappstore = useAuthStore()
                authappstore.isAppOK = true;
                sessionStorage.setItem("accesstoken",res.data.accesstoken);
                sessionStorage.setItem("refreshtoken", res.data.refreshtoken);
                req.headers.Authorization = `Bearer ${res.data.accesstoken}`;
                if (requestArray.length !== 0) {
                    requestArray.forEach(x => {
                        try {
                            console.log(x);
                            axios(x)
                        } catch (e) {
                            console.log(e)
                        }
                    });
                    requestArray = [];

                }
            }) .catch((err) => {
                const errStatus = err.statusCode || 500;
                const errMsg = err.message || 'Something went wrong';
                console.log(errMsg)
            }) .finally(() => {
                busy = false;
            });
        }


    }
    else {
        return req
    }
    


})

export default axios;
解决方案

核心问题分析

  1. 原拦截器在Token过期后,仅将请求推入数组,未返回等待Token刷新完成后再执行的Promise,导致原请求(来自Pinia)无法获取后续响应。
  2. 重新发起请求时,直接调用axios(x)但未将结果关联到原请求的Promise链,Pinia发起的请求一直处于pending状态。
  3. 全局刷新状态管理不严谨,可能导致重复触发刷新请求。

修正后的代码

import axios from 'axios';
import { useAuthStore } from "@/stores/Authentication/AppAuthStore.js";
import VueJwtDecode from 'vue-jwt-decode';

axios.defaults.baseURL = import.meta.env.VITE_API_ENDPOINT;
axios.defaults.maxRedirects = 0;
// 用全局Promise存储正在进行的刷新请求,避免并发重复刷新
let refreshPromise = null;
const baseURL = import.meta.env.VITE_API_ENDPOINT;

axios.interceptors.request.use(async req => {
    if (req.url.endsWith("refresh")) {
        return req;
    }

    const accesstoken = sessionStorage.getItem("accesstoken");
    if (!accesstoken) {
        return req;
    }

    req.headers.Authorization = `Bearer ${accesstoken}`;
    const user = VueJwtDecode.decode(accesstoken);
    const isExpired = Date.now() >= ((user.exp * 1000) - 10000);

    if (!isExpired) {
        return req;
    }

    // 如果正在刷新Token,等待刷新完成
    if (refreshPromise) {
        await refreshPromise;
        // 刷新完成后更新当前请求的Token
        req.headers.Authorization = `Bearer ${sessionStorage.getItem("accesstoken")}`;
        return req;
    }

    // 开始刷新Token
    refreshPromise = new Promise(async (resolve, reject) => {
        try {
            const refreshtoken = sessionStorage.getItem("refreshtoken");
            const res = await axios.post(`${baseURL}auth/refresh`, {
                refresh_token: refreshtoken
            });

            console.log("token refreshed : " + res.data.accesstoken);
            const authappstore = useAuthStore();
            authappstore.isAppOK = true;
            sessionStorage.setItem("accesstoken", res.data.accesstoken);
            sessionStorage.setItem("refreshtoken", res.data.refreshtoken);
            
            resolve(res.data.accesstoken);
        } catch (err) {
            const errStatus = err.response?.status || 500;
            const errMsg = err.response?.data?.message || 'Something went wrong';
            console.log(errMsg);
            reject(err);
        } finally {
            // 刷新完成后重置刷新Promise
            refreshPromise = null;
        }
    });

    // 等待刷新完成,更新当前请求的Token后返回
    await refreshPromise;
    req.headers.Authorization = `Bearer ${sessionStorage.getItem("accesstoken")}`;
    return req;
});

export default axios;

关键改动说明

  1. 用全局refreshPromise管理刷新状态:避免多个并发请求重复触发Token刷新,所有过期请求都会等待同一个刷新Promise完成。
  2. 将原请求的执行逻辑绑定到刷新Promise:Token过期时,原请求会等待刷新完成,更新请求头后继续执行,确保响应能正常返回给Pinia。
  3. 去掉冗余的requestArray:通过Promise链自动处理所有等待的请求,无需手动存储和遍历请求配置。
  4. 完善错误处理:捕获刷新请求的错误并传递给原请求,避免请求静默失败。

内容的提问来源于stack exchange,提问作者John Lauwers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:20:33