袁来如此的工作笔记
袁来如此的工作笔记
竹杖芒鞋轻胜马,谁怕? 一蓑烟雨任平生。

前端实现token刷新

浏览量:182

what:

why:

how:

原理

  1. 登录,从后台获取到token(鉴权令牌),refresh_token(刷新token的令牌),expire_time(token的时效)。将这三个以及登录的时间点(loginTime)存储下来,以备使用。

  2. 用户操作中,向后台发送请求,每次请求时,将当前请求时间(currentTime)与loginTime和expire_time对比,
    即(currentTime-loginTime)得到的时间段即将接近或超出expire_time时,使用refresh_token去重新获取token。
    注:此处需要知道的是,refresh_token与token一样,都是有时效的。但refresh_token的时效必定长于token,这样token即便过期了,也不会影响refresh_token。因此只要用户在refresh_token的有效期内向后台发送请求,token就可以一直得到刷新。
    而用户长时间未操作,refresh_token也过期了,这时候就可以被正常登出。

  3. 使用refresh_token去重新获取token的操作实际上就是再次进行了一次登录操作,只不过这次的参数并非账密,而是refresh_token,
    并且这个操作用户是不知情的。每次登录获取到的鉴权信息都会覆盖上一次存储的鉴权信息,这样就会确保token和refresh_token一直都是最新的。

大体流程就是以上三个步骤循环。


import axios, { AxiosInstance } from \axios\
import router from \@/router\
import store from \@/store\
import { message } from \ant-design-vue\
import { getToken } from \@/utils/auth\
import { refreshToken } from \@/utils/refreshToken\
 
// create an axios instance
const service = axios.create({
  baseURL: \/api\,
  timeout: 30000
})
 
// 请求拦截器
service.interceptors.request.use(
  (config: any) => {
    if (store.getters.token) {
      config.headers[\Authorization\] = getToken()
    }
    // 登录,不校验token
    if (config.url.indexOf(\/login\) > -1) {
      return config
    } else {
      let interval = null
      let retry = new Promise((resolve, reject) => {
        const refreshFun = () => refreshToken().then(res => {
          // 判断是否刷新token,且成功了
          if (res === \success\) {
            console.log(\刷新token!!!\)
            config.headers[\Blade-Auth\] = `Bearer ${getToken()}`
            // 挂起请求
            resolve(config)
          } else if (res === \pending\) {
            console.log(\等待刷新token!!!\)
            interval = setInterval(() => {
              refreshFun()
            }, 500)
          } else { // 不需要刷新token或刷新失败
            // 等待刷新成功后就不需要再刷新,此时需重新赋值新token
            if (interval) {
              console.log(\等待成功!\)
              config.headers[\Blade-Auth\] = `Bearer ${getToken()}`
              clearInterval(interval)
            }
            // 挂起请求
            resolve(config)
          }
 
        })
 
        refreshFun()
      })
      return retry
    }
  },
  error => {
    return Promise.reject(error)
  }
)
 
// 返回拦截器
service.interceptors.response.use(
  response => {
    if (response.data.success) {
      // 若为登录接口,记录登录返回的时间
      if (response.config.url.indexOf(\/login\) > -1) {
        const time = String(new Date().getTime())
        localStorage.setItem(\loginTime\, time)
      }
      return Promise.resolve(response.data)
    }
    response.data.msg && message.error(response.data.msg)
    return Promise.reject(response.data.msg)
  },
  error => {
    if (error.response.status && error.response.status === 401) {
      store.dispatch(\user/logout\)
      router.push(\/login\)
    }
    error.response.data.msg && message.error(error.response.data.msg)
    return Promise.reject(error)
  }
)
 
export default service as AxiosInstance


打赏