如何挂起Promise请求,refresh_token后再用新的access_token重新发起请求?

接手老项目,需要写一个access_token刷新的逻辑,具体流程我就不赘述了,网上关于JWT刷新流程的文章有很多。我遇到的主要问题是,项目没有使用axios,原生的fetch没有拦截器,对于多次同时刷新token的请求是应该做拦截处理的,待第一个刷新请求回调后再发起后续被拦截请求,业务场景和这篇文章类似,难点在于如何挂起请求,直接贴代码。

  let isRefreshing = false; // 用于拦截鉴权失败的请求
  let pendingRequests = []; // 被拦截请求的缓存池

  // 持久化token,我是写cookie里的
  const storeToken = function (data) {
    const { access_token, refresh_token } = data;
    const duration = 60 * 60 * 1; // 持续时间-秒
    ctx.app.$cookie('accessToken', access_token, { expires: duration });
    ctx.app.$cookie('refreshToken', refresh_token, { expires: duration });
  };

  const refreshToken = async function () {
    isRefreshing = true;
    try {
      // 换取token的请求
      const res = await $jfetch.post('/japi/v1/auth?grant_type=refresh_token', {
        body: {
          refresh_token: ctx.app.$cookie('refreshToken'),
        },
      });
      storeToken(res.data);
      isRefreshing = false;
      const newAccesssToken = res.data.access_token;
      // 用新的token重新发起待定池中的请求
      pendingRequests.forEach((item) => {
        item.resolved(newAccesssToken);
      });
      // 清空缓存池
      pendingRequests = [];
      return newAccesssToken;
    } catch (error) {
      isRefreshing = false;
      return null;
    }
  };

  const getCookieToken = async function () {
    // 避免重复发起刷新
    if (isRefreshing) return;
    const accessToken = ctx.app.$cookie('accessToken');
    if (!accessToken) {
      return await refreshToken();
    }
    return accessToken;
  };

  const getAccessToken = async function () {
    // 取到为空的表示是该被拦截的
    const accessToken = await getCookieToken();
    // 将被拦截的请求挂起 存到缓存池中
    if (!accessToken) {
      // 重点
      const externalControl = {
        resolved: null,
      };
      // 这里返回了一个新的Promise变相的实现请求的挂起(只要没有resolved或rejected,请求就会一直处于pedding状态)
      // 并将Promise状态的改变放到了外部一个对象来控制 externalControl ,待定池缓存这个对象即可,待需要执行后续被拦截请求,只需要利用这个对象引用的 resolved 来改变Promise状态即可实现请求挂起的放行
      const interceptPromise = new Promise((resolved) => {
        externalControl.resolved = resolved;
      });
      pendingRequests.push(externalControl);
      return interceptPromise;
    }
    return accessToken;
  };

在需要鉴权的接口调用,这里还缺少refresh_token失效跳转到登录页的逻辑,自行填补:
headers['Authorization'] = await getAccessToken();

你可能感兴趣的:(如何挂起Promise请求,refresh_token后再用新的access_token重新发起请求?)