双token无感刷新机制

借鉴: https://blog.csdn.net/m0_48468380/article/details/121577011

流程图

贴下封装的request.js代码

import axios from "axios";
import JsCookie from "js-cookie";
import { ElMessage } from 'element-plus'

// 创建并配置一个新的axios
const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_API,
    timeout: 60000, // 请求超时时间 毫秒
    withCredentials: true,   // 异步请求时是否携带cookie,跨域请求设置
    headers: {   // 设置后端需要的传参类型
        "Content-Type": "application/json", //设置post请求头
        "Client": "front",  // 所属客户端
        // "Authorization": "",
        // "X-Requested-With": "XMLHttpRequest",
    },
});

//service.defaults.withCredentials = true; //跨域请求设置
//axios.defaults.headers.post["Content-Type"] = "application/json"; //设置post请求头

// 添加请求拦截器
service.interceptors.request.use(
    (config) => {
        // 在发送请求之前做些什么。。。
        let tokenInfo = ""
        if(!config.url.includes('userLogin/refreshtoken')){  //判断是否为刷新token请求--否
            tokenInfo = JsCookie.get("accessToken") ? JSON.parse(JsCookie.get("accessToken")) : ""
        }else{  //判断是否为刷新token请求--是
            tokenInfo = JsCookie.get("refreshToken") ? JSON.parse(JsCookie.get("refreshToken")) : ""
        }
        if(tokenInfo){  //配置请求token
            config.headers['Authorization'] = tokenInfo
        }

        return config;
    },
    (error) => {
        // 对请求错误做些什么
        return Promise.reject(error);
    }
);

let isTokenRefreshing = false  // token是否正在刷新

let requestList = []  //正在等待请求的接口队列

// 添加响应拦截器
service.interceptors.response.use(
    (response) => {
        const res = response.data;

        if (res.code == 100009 || res.code == 100005) {  // 对通用token令牌过期的处理
            if(!isTokenRefreshing){  //判断当前不处于刷新阶段--则进行刷新操作
                isTokenRefreshing = true  // 上锁

                const requestconfig = {
                    url: `/iam/userLogin/refreshtoken`,
                    method: "get",
                }
                return service(requestconfig).then(_res => {
                    if(_res.code == 200000){  //刷新成功--将新的通用token和刷新token都进行更新;并执行requestList等待队列里的接口请求
                        // 保存通用token
                        let accessTokenExpireTime = new Date(new Date().getTime() + _res.data.expire_in)
                        JsCookie.set("accessToken", JSON.stringify(_res.data.access_token), { expires: accessTokenExpireTime });
                        // 保存刷新token
                        let refreshTokenExpireTime = new Date(new Date().getTime() + _res.data.refresh_expire_in)
                        JsCookie.set("refreshToken", JSON.stringify(_res.data.refresh_token), { expires: refreshTokenExpireTime });

                        //执行后续推入到requestList队列中的请求,(requestList中存的不是请求参数,而是请求的Promise函数,这里直接拿来执行就好)
                        requestList.forEach(run => run())
                        //将请求队列置空
                        requestList = []

                        //重新执行一下本次未执行成功的请求并返回
                        return service(response.config);
                    }else if(_res.code == 100010){  //刷新token也过期了--移除用户登录相关信息,并提示用户重新登录
                        JsCookie.remove("accountInfo"); // 移除用户信息
                        JsCookie.remove("accessToken"); // 移除通用token
                        JsCookie.remove("refreshToken"); // 移除刷新token
                        ElMessage.warning("您尚未登录或登录已过期,请重新登录哦");
                        return Promise.reject(new Error(_res.msg || "Error"));
                    }
                }).catch(()=>{

                }).finally(()=>{
                    isTokenRefreshing = false  //解锁
                })
            }else{  //判断当前处于token刷新阶段--将后续接口推入等待请求队列,等待刷新token接口请求完毕后执行后续请求队列
                return new Promise(resolve => {
                	//这里加入的是一个promise的解析函数,将响应的config配置对应解析的请求函数存到requestList中,等到刷新token回调后再执行
                    requestList.push(() => {
                        resolve(service(response.config));
                    })
                })
            }
        } else {
            // 其他情况则返回结果,对应状态code需在具体请求函数里判断
            return res;
        }
    },
    (error) => {
        ElMessage.warning(error.msg || "服务器开小差了呢,请稍后再试~");
        return Promise.reject(error);
    }
);

export default service;

posted @ 2022-12-15 09:59  huihuihero  阅读(755)  评论(0编辑  收藏  举报