在vue项目中实现Token替换和请求拦截

        Token在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。

        在vue项目中实现Token替换----前端学海

        这里使用的vue中的请求拦截器,根据后端返回的Token的生成时间,以及该Token的有效期和当前发送请求的时间进行计算,判断当前Token是否快要过期,以确定是否开始请求新的Token。

计算公式为:Token有效时间 -(当前时间 - Token的生成时间)= 剩余的Token有效时间

        这里我的判断为,Token剩余有效时间,小于五分钟的时候,调用刷新Token的接口,请求最新的Token。

在请求新Token期间,可能同时会有好几个请求同时发起,我们在这里将这些请求拦截下来,通过Promise,将这些请求,完整的防在待执行的请求队列中,

当新的Token请求成功,更新本地的Token之后,开始执行拦截的请求队列,并将这些请求的header中的Token替换为最新的Token,完成Token整个流程。

如果请求新Token接口失败,并且原Token已过了有效期时,提示登录过期,重定向到登录页,重新登录!

        如有建议,欢迎在评论区指出!!!

请看代码

import axios from 'axios' // 引入axios

import loginApi from './api/article/loginApi'

import router from '../router'

import qs from 'qs'

import {

  Message

} from 'element-ui'

console.info(location.hostname)

if (location.hostname === 'localhost' ||

  location.hostname === '127.0.0.1') {

  axios.defaults.baseURL = '/api/yifd'

} else {

  axios.defaults.baseURL = `https://${location.hostname}/proxy`

}

// eslint-disable-next-line no-unused-vars

/* 是否正在刷新的标志 */

window.isRefreshing = false

// token是否过期,默认为否

window.tokenOverdue = false

/* 存储请求的数组 */

const refreshSubscribers = []

/* 将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...] */

function subscribeTokenRefresh (cb) {

  refreshSubscribers.push(cb)

}

/* 数组中的请求得到新的token之后自执行,用新的token去请求数据 */

function onRrefreshed (token) {

  refreshSubscribers.map(cb => cb(token))

}

function tokenIsOverdue () { // token是否过期

  const time = window.sessionStorage.getItem('tokenTime') // token有效期,秒级,后端返回,浏览器存储的

  const queryTime = parseInt(window.sessionStorage.getItem('tokenQueryTime')) // token获取的时间,秒级

  const nowTime = new Date().getTime()

  const arealyTime = Math.ceil(nowTime / 1000) - queryTime // 已经过去的时间

  // console.log(time, queryTime, Math.ceil(nowTime / 1000), arealyTime)

  if ((time - arealyTime) < 300) { // token五分钟内即将过期时开启替换token,目前后台的token有效期为30分钟。

    return true

  } else {

    return false

  }

}

tokenIsOverdue()

/**

* 请求失败后的错误统一处理

* @param {Number} status 请求失败的状态码

*/

const errorHandle = (response) => {

  const status = response.data.code

  const msg = response.data.msg

  // 状态码判断

  switch (status) {

    // 401:未登录状态或token过期,跳转登录页

    case 401:

      Message.error(msg)

      break

    case 403:

      alert(msg)

      break

      // 404:请求不存在

    case 404:

      router.push({

        path: '/404'

      })

      Message.error('请求的资源不在')

      break

      // 服务器错误

    case 500:

      Message.error(msg)

      break

    default:

      console.log(msg)

  }

}

// 创建axios实例

var instance = axios.create({

  timeout: 1000 * 100

})

/**

* 请求拦截器

* 每次请求前,如果存在token则在请求头中携带token

*/

instance.interceptors.request.use(

  request => {

    // // 该位置会获取登陆成功时的token数据

    const token = window.sessionStorage.getItem('token')

    /* 判断用户是否已经登录 */

    if (token) {

      /* 请求头添加token信息 */

      request.headers.Authorization = token

      if (request.url.includes('ossUpload')) {

        request.timeout = 600000

      } else {

        request.timeout = 100000

      }

      /* 判断token是否即将过期 */

      /*  `oauth/token`是刷新token的接口,只有当token将要过期且不是请求刷新token的接口才会进入 */

      if (tokenIsOverdue() && !request.url.includes('oauth/token')) {

        /* 首先所有的请求来了,我们要先判断当前是否正在刷新,如果不是,将刷新的标志置为true并请求刷新token;如果是,将请求存储到数组中 */

        if (!window.isRefreshing) {

          window.isRefreshing = true

          loginApi.apiLogin(

            qs.stringify({

              grant_type: 'refresh_token',

              refresh_token: window.sessionStorage.getItem('refresh'),

              client_id: 'trade',

              client_secret: 'homedone'

            })

          ).then(res => {

            if (res.data.code === 200) {

              /* 将刷新的token替代老的token */

              request.headers.Authorization = 'Bearer ' + res.data.data.access_token

              /* 更新内存的token信息 */

              const resData = res.data.data

              window.sessionStorage.setItem('token', 'Bearer' + ' ' + resData.access_token) // 存入token Bearer

              window.sessionStorage.setItem('refresh', resData.refresh_token) // 存入刷新token,用来替换过期token

              window.sessionStorage.setItem('tokenTime', res.data.data.expires_in) // token有效期,秒级

              window.sessionStorage.setItem('tokenQueryTime', Math.floor(res.data.timestamp / 1000)) // token获取时间,秒级

              /* 执行数组里的请求,重新发起被挂起的请求 */

              onRrefreshed(res.data.data.access_token)

            } else { // 目前做的处理,当替换token请求失败时

              if (res.status === 401) { // 当刷新token时为401,标识refresh_token也已经过期,需重新登录

                Message.warning('登陆过期请重新登陆!')

                window.location = '#/' // 重定向回登录页

                window.sessionStorage.clear() // 清除包括token的所有缓存,让用户重新登录

              } else {

                window.isRefreshing = true

              }

            }

          })

          const retry = new Promise((resolve, reject) => {

            subscribeTokenRefresh((token) => {

              request.headers.Authorization = 'Bearer ' + token

              /* 将请求挂起 */

              resolve(request)

            })

          })

          return retry

        }

      } else {

        return request

      }

    } else {

      /* 如果没有登录直接返回请求 */

      return request

    }

    return request

  },

  error => {

    return Promise.reject(error)

  }

)

// 响应拦截器

instance.interceptors.response.use(

  // 请求成功

  res => {

    res.status === 200 ? Promise.resolve(res) : Promise.reject(res)

    return res

  },

  // 请求失败

  error => {

    const {

      response

    } = error

    if (response) {

      // 请求已发出,但是不在2xx的范围

      errorHandle(response)

      return Promise.reject(response)

    } else {

      return Promise.reject(error)

    }

  }

)

export default instance

你可能感兴趣的:(在vue项目中实现Token替换和请求拦截)