oi!记录一下前端实现无感刷新token的方法~

173 阅读1分钟

需求背景

在实现单点登录时,结合使用token无感刷新,提升用户体验。

调用接口时,如果token过期(即接口返回401),则去调用refreshtoekn接口去刷新token,此时如果sessionId有效则返回新token,并重新发起当前请求,实现无感刷新token;如果sessionId失效则会返回401。

解决思路

当请求返回401时,表示token过期,此时需要用旧token换新token,如果拿到新token,则存储并设置新token后,重发请求

  • 需要考虑并发情况,即多个请求返回401。设置一个字段(isRefreshing)来作为一个“锁”,数组(failedQueue)来存储发送失败的请求

如果refreshtoken返回401的话,走正常token过期登出处理逻辑。

具体实现

在响应拦截器中对401情况进行统一处理

// 是否正在进行刷新token
let isRefreshing = false
// 失败请求队列
let failedQueue = []

service.interceptors.response.use(async res => {}, async error => {
    ...
    if (error.response.status === 401) {
        if(config.url==='/sso/refresh'){
        // 当refreshtoken返回401时,走正常登出流程
        // 比如:弹框提示 -> logout -> 清空token -> 跳转首页
        ...
        }
    }else{
        // 如果正在刷新Token,将当前请求加入队列,等待Token刷新完毕
        if(isRefreshing) {
          return new Promise((resolve, reject) => {
            failedQueue.push({ resolve, reject, originalRequest:error.config});
          })
        }
        
        isRefreshing = true
        return refreshToken().then(res=>{
          const newToken = res.token;
          setToken(newToken)
          service.defaults.headers['Authorization'] = `Bearer ${newToken}`;
          // 处理失败的队列请求
          failedQueue.forEach((req) => req.resolve(service(req.originalRequest)));
          // 清空队列
          failedQueue = [];
          // 重发当前请求
          return service(error.config);
        }).catch(err=>{
          failedQueue.forEach((req) => req.reject(err));
          failedQueue = [];
          return Promise.reject(err);
        }).finally(()=>{
          isRefreshing = false;
        })
    }
    ...
})