解决js异步同时请求接口的问题

332次阅读
没有评论

最近在实现refesh token,但是在前端页面请求刷新的时候遇到一个问题。

场景如下:刷新页面时,同时有几个接口在进行请求,由于refesh token的操作放在拦截器中,在接口请求时,判断当前token是否过期,如果token过期之后,进行token刷新操作。可想而知会发生什么问题。几个接口争先恐后的去使用refesh_token换取新的token,跑的快的,成功拿到新的token。跑的慢,只有拿第一个接口用过的refesh_token去刷新,服务器看到递过来的是已经用过了的 refesh_token,只有毫不犹豫的拒绝。

解决js异步同时请求接口的问题
请求失败

所以就需要一个机制,让换取token的操作依次执行,或者只需要执行一次。让第一次执行刷新token之后,后面接口发现token已经被刷新了,就不在去刷新token。

这时候就可以利用js Promise链式调用的特点去解决这个问题。具体思路就是返回Promise对象,通过调用Promise的then函数,返回需要的内容。具体原理的话,我描述不清楚,因为也是是个前端小菜鸡啊~~~所以有啥错误的地方,还请多多指教和包含。

上代码:

1、拦截器中判断token是否过期,过期就操作刷新

    let token = store.getters["account/token"];
    let timeNow = Math.round(new Date().getTime() / 1000);
    if (!requestUrlInIgnoreList(url) && (!token || token.expires_at - 60 <= timeNow)) {
      console.log("[Local]AccessToken expiring, start refresh token...");
      let newToken = await localRefreshToken();
      if (newToken) {
        console.log("[Local]Refresh successful, token is " + JSON.stringify(newToken));
        config.headers.Authorization = `Bearer ${newToken.access_token}`;
      } else {
        message.warning("认证已过期,请重新登录");
      }
    } else {
      if (!requestUrlInIgnoreList(url)) {
        config.headers.Authorization = `Bearer ${token.access_token}`;
      }
    }

2、token刷新类

class localAccount {
  refreshTokenPromise = null;

  refreshToken = async () => {
    if (this.refreshTokenPromise) {
      console.log("我是后来的,我也要刷新token...");
      return this.refreshTokenPromise.then(() => {
        let lastRefeshToken = store.getters["account/token"];
        return lastRefeshToken;
      });
    }
    let newToken = undefined;
    let oldToken = store.getters["account/token"];
    if (!oldToken || !oldToken.refresh_token) {
      return newToken;
    }
    this.refreshTokenPromise = request(POST_REFESH, METHOD.POST, {
      Token: oldToken.refresh_token,
    })
      .then((result) => {
        if (result && result.data.code == 0) {
          let userToken = {
            access_token: result.data.data.accessToken,
            refresh_token: result.data.data.refreshToken,
            token_type: result.data.data.tokenType,
            expires_at: result.data.data.expiresAt,
            profile: result.data.data.profile,
          };
          newToken = userToken;
          //设置用户
          if (newToken) {
            store.commit("account/setToken", newToken);
          }
          this.refreshTokenPromise = null;
          return newToken;
        }
      })
      .catch((err) => {
        console.error(err);
      });
    return this.refreshTokenPromise;
  };
}

通过定义一个全局的 refreshTokenPromise 对象,第一次请求时, refreshTokenPromise 对象为空,进行token刷新。由于封装的request本身也是一个异步请求,所以将request赋值给 refreshTokenPromise 。所以当第二个刷新请求过来之后,判断 refreshTokenPromise 是否为空,不为空的话,表示正在请求。使用then方法,返回从store取出第一次请求更新的token。

管理员
版权声明:本站原创文章,由管理员2021-08-20发表,共计2192字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
载入中...