前端刷新token最佳实践

背景需求

客户端登录请求成功后,服务器将用户信息(如用户id)使用特殊算法加密后作为验证的标志发送给用户(即token),当用户下次发起请求时,会将这个token捎带过来,服务器再将这个token通过解密后进行验证,通过的话,则向客户端返回请求的数据;反之,则请求失败

问题表现

一般为了安全性,token都会设置一个过期时间,在过期之后就无法请求相关接口了,这时应该怎么办呢,是直接退出登录吗?

解决方案

方案一

后端返回过期时间,前端判断token过期时间,去调用刷新token接口

方案二

写个定时器,定时刷新token接口

方案三

在请求拦截器中拦截,判断token是否将要过期,调用刷新token接口

方案一需要后端提供接口,客户端请求增加带宽,方案二客户端一直刷新,造成资源浪费,为了更好的用户体验,我们选择方案三,在拦截器里拦截刷新token

具体操作

登录请求成功后,会返回一个token和token过期时间,在每次请求api时,前端可以先判断一下token是否即将过期或已过期,如果是,则请求刷新token的接口,成功替换原来的token之后才可以重新发起请求

...
// 是否有请求正在刷新token
window.isRefreshing = false;
// 被挂起的请求数组
let refreshSubscribers = [];
// 刷新请求(refreshSubscribers数组中的请求得到新的token之后会自执行,用新的token去请求数据)
function onRrefreshed(token) {
  refreshSubscribers.map(cb => cb(token));
}
// push所有请求到数组中
function subscribeTokenRefresh(cb) {
  refreshSubscribers.push(cb);
}
// http request拦截
axios.interceptors.request.use(config => {
  // 开启 progress bar
  NProgress.start();
  const meta = (config.meta || {});
  const isToken = meta.isToken === false;
  config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;
  
  // 判断token是否将要过期
  if (isTokenExpired()) {
    // 判断是否正在刷新
    if (!window.isRefreshing) {
      // 将刷新token的标志置为true
      window.isRefreshing = true;
      // 发起刷新token的请求
      this.$store
          .dispatch("refreshToken")
          .then(res => {
            // 将标志置为false
            window.isRefreshing = false;
            // 执行数组里的函数,重新发起被挂起的请求
            onRrefreshed(res.data.data.token);
            /*执行onRefreshed函数后清空数组中保存的请求*/
            refreshSubscribers = [];
          })
          .catch(() => {
            /*将标志置为false*/
            window.isRefreshing = false;
          });
    }
    /*把请求(token)=>{....}都push到一个数组中*/
    let retry = new Promise((resolve) => {
      /*(token) => {...}这个函数就是回调函数*/
      subscribeTokenRefresh(() => {
        config.headers.Authorization = "Bearer " + getToken();
        /*将请求挂起*/
        resolve(config);
      });
    });
    return retry;
  }
  return config
}, error => {
  return Promise.reject(error)
});
...
复制代码

猜你喜欢

转载自juejin.im/post/7077757706743840782