• 解决token过期的问题
  • 发布于 1周前
  • 42 热度
    0 评论
  • 望北海
  • 0 粉丝 3 篇博客
  •   
token何时刷新问题,一直都困扰着我,以前也有朋友给我一些解决方案,但始终都不满意,如:1.定时刷新token(10分钟刷一次,只要比过期时间短),2.token有效期延长(十几天)。这或多或少都存在着一些问题,直到这些天我才找到和自己想法类似的文章,并加以修改,分享给你们。

这是请求与响应的过程:
1.前端发起ajax请求
2.后端发现token已经过期,返回相应的状态码
3.前端拦截响应数据,并发起刷新token的请求
4.拿到最新的token,保存到本地
5.拿到最新的token去进行刚刚未请求成功的接口
6.获取到刚刚请求的结果,覆盖第一次请求失败的响应数据

下面是解决何时刷新token的核心方法,写在main.js文件中
//用于重新发送上一条请求
axios.request(response.config);
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import axios from 'axios';
import store from './store/index';
import common from './utils/common.js';
import api from './utils/api.js';
import ElementUI from 'element-ui';
// 堆代码 duidaima.com
...省略一些代码

// 是否正在刷新的标记
let isRefreshing = false;
// 每一项将是一个待执行的函数形式
let requests = [];
/* 添加一个返回拦截器 */
axios.interceptors.response.use(async (response) => {
    // 对返回的数据进行一些处理
    // console.log(router.currentRoute.fullPath);
    console.log(response.data);
    if (response.data.code === "1001") {
      /* accessToken失效 */
      response.config.headers.refreshToken = localStorage.getItem('refreshToken');
      /* 利用refreshToken刷新accessToken */
      if (!isRefreshing) {
        isRefreshing = true;
        //刷新token方法,等待刷新token方法执行返回后在执行后面的代码
        const refreshToken = await api.api.postRefreshToken({});
        /* 重新赋值accessToken */
        localStorage.setItem(
          "Authorization",
          "Bearer " + refreshToken.data.accessToken
        );
        /* 重新发送上一条请求(这条请求不会再下的else代码中拦截),await不需要加,无需异步完成以后在执行后面函数 */
        axios.request(response.config);
        requests.forEach(request => request());
        requests = [];
        isRefreshing = false;
      } else {
        // 正在刷新token,将返回一个未执行resolve的promise
        // 拦截其他所有的请求,使其处于一个未执行的状态,只有当刷新token请求返回以后,再执行拦截的所有请求
        return new Promise((resolve) => {
          // 将resolve放进数组,用一个函数形式来保存,等token刷新后直接执行
          requests.push(() => {
            resolve(axios.request(response.config))
          })
        })
      }
    } else if (response.data.code === "1002") {
      /* refreshToken失效 这个code需要再修改 */
      if (!isRefreshing) {
        isRefreshing = true;
        ElementUI.MessageBox.confirm('登录时间过长,请重新登录?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          /* 跳转到登录页,而后登录成功,还返回当前页 */
          router.replace({
            path: '/login',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
          isRefreshing = false;
        }).catch(() => {
          /* 不做处理,只是为了去除页面上的报错 */
        });
      }
    } else {
      return response;
    }
  },
  (error) => {
    /* 对返回的错误进行一些处理 */
    return Promise.reject(error);
  });
大致的思路就如代码中所写的,具体情况还要看你们自己后端是如何实现,如1.请求时发现token过期时只判断accessToken过期,不判断refreshToken过期,只有在刷新accessToken的时候才判断refreshToken,从而进行重新登录。2.在刷新accessToken的时候,refreshToken刷不刷,是否要一段时间后需要重新登录,还是用户活跃一直不需要重新登录,看你们自己的需求与考虑。

以上代码,解决token何时何地,无痛刷新问题,解决并发请求多次刷新token问题,解决refreshToken过期,重新登录问题并跳转到登录前页面,也解决了每个组件都判断token过期问题,删除了大量的重复代码。

理解async 函数,promise对象中pending状态的特性,才是理解本文的关键。测试代码就不贴了,亲测有效,有问题再留言咯。
用户评论