token过期 如何使用refresh_token实现无感刷新页面?

步骤如下:

1-token过期根据refresh_token获取新的token 重新获取数据

2-创建一个新的axios实例 【使用request防止再次进入请求拦截和请求响应而进入死循环】

3-根据请求相应的响应值 是不是401 是:说明token过期

然后进行判断store中的 user :{token:’*****’,refresh_token:’******’}中的 refresh_token和user对象是否存在 ,如果不存在说明之前没有登录过,直接去登录

4-使用新创建的axios 实例对象 requestFreshToken 发送新的请求 headers中的口令携带的是 refresh_token

5-获取token之后 将值重新赋值给user中的token

6-将user重新存入store中

7-重新获取刚才因为token失效而没有获取的数据 直接使用request 参数 来自error对象中【这里保存了之前token失效的请求数据】

具体实现 代码如下:

import Vue from 'vue'
import axios from 'axios'
import router from '@/router'
import {
  ACCESS_TOKEN,
  FRESH_TOKEN
} from '@/store/mutation-types'
import {
  Message
} from 'element-ui';
// somefile.js
import ls from '@/utils/localStore.js';
const defaultSettings = require("@/settings.js");


let apiBaseUrl = process.env.VUE_APP_BASE_API

let isRefreshing = false //是否正在刷新token

let callbacks = [] //失效后同时发送请求的容器 --- 缓存接口
// 刷新 token 后, 将缓存的接口重新请求一次
function onAccessTokenFetched() {
  callbacks.forEach(callback => {
    callback()
  })
  callbacks = [] // 清空缓存接口
}

// 添加缓存接口
function addCallbacks(callback) {
  callbacks.push(callback)
}
// 请求函数
function request(url, options) {
  const token = ls.get(ACCESS_TOKEN)
  const defaultOptions = {
    withCredentials: true,
    url: url,
    baseURL: apiBaseUrl,
  };
  const newOptions = {
    ...options,
    ...defaultOptions
  };


  newOptions.headers.token = token;
  return axios.request(newOptions)
    .then(checkStatus)
    .catch(error => console.log(error));
}

function checkStatus(error) {
  if (error.response) {
    const response = error.response
    const refreshToken = ls.get(FRESH_TOKEN);
    if (response && response.status == 401) {
      // 刷新token的函数,这需要添加一个开关,防止重复请求
      if (!isRefreshing) {
        isRefreshing = true;
        axios({
          baseURL: apiBaseUrl,
          url: `/reLogin/${refreshToken}`,
          method: 'post'
        }).then(response => {
          const res = response.data;
          isRefreshing = false;
          if (res.code == '200') {
            ls.set(ACCESS_TOKEN, res.data.access_token)
            onAccessTokenFetched()
          } else {
            // 登出
            ls.clear()
            router.replace({
              path: '/login'
            })
          }
        }).catch(error => {
          console.log("刷新token异常", error)
          // 跳转到首页
          ls.clear()
          console.log('request router', router)
          router.replace({
            path: '/login'
          })

        });
      }
      isRefreshing = true;
      const options = response.config;
      const url = options.url.replace(process.env.VUE_APP_BASE_API, '')
      // 这个Promise函数很关键
      const retryOriginalRequest = new Promise((resolve) => {
        console.log('resolve', resolve)
        addCallbacks(() => {
          resolve(request(url, options))
        })
      });
      return retryOriginalRequest;
    } else {
      // message 诺到里面,401不报错
      Message.closeAll()
      Message({
        message: response.data.msg,
        type: 'error'
      }, true)
      return Promise.reject(error)
    }
  }
  return error.data;
  // return error;

}

// 创建一个axios实例
const service = axios.create({
  baseURL: apiBaseUrl,
  withCredentials: true, // 跨域请求时发送Cookie
  timeout: 50000 // request timeout
})

//请求拦截器
service.interceptors.request.use(
  config => {
    // 在发送请求之前做一些事情
    if (ls.get(ACCESS_TOKEN)) {
      // 让每个请求都带有token
      config.headers['token'] = ls.get(ACCESS_TOKEN)
    } else if (process.env.NODE_ENV === 'development' && defaultSettings.noToken) {
      // console.log('没有token--------', process.env.NODE_ENV)
      config.headers['swagger_user_name'] = defaultSettings.swagger_user_name
      config.headers['swagger_user_pwd'] = 1
    }
    config.autoCatchErr = true;
    return config
  },
  error => {
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data;
    // 增加res.response,zq-el-ui中的下载请求会用到
    res.response = response;
    if (response.status == 200 && response.config.params && response.config.params.no_interception_response) {
      return response.data
    }
    // 文件下载
    if (response.status == 200 && response.request.responseType == "blob" || response.request.responseType == "arraybuffer") {
      return response.data
    }
    // 正常返回
    if (response.data.body && response.data.body.rtnCode === '0000') {
      return response.data.body.dataSet
    }
    if (res.code == 200) {
      if (res.msg) {
        Message({
          message: res.msg,
          type: 'success'
        }, true)
      }
      return res
    } else {
      if (response.config.autoCatchErr) {
        Message.closeAll()
        Message({
          message: res.msg,
          type: 'error'
        }, true)
        console.log('请求失败', res)
        // 
        // throw new Error('接口报错:::', res);
        return res
      } else {
        Message({
          message: res.msg,
          type: 'error'
        }, true)
        return res

      }
    }
  },
  error => {
    return checkStatus(error)
  }
)

export default service

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值