前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何挂起Promise请求,refresh_token后再用新的access_token重新发起请求?

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

作者头像
用户2141756
发布2020-04-09 11:52:37
1.2K0
发布2020-04-09 11:52:37
举报
文章被收录于专栏:滕先生的博客滕先生的博客

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

代码语言:javascript
复制
  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();

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档