最简答的实现 无感刷新token

364 阅读1分钟

思路:

  1. 在 token 即将过期前主动刷新(微信小程序文档中的推荐方式)
  2. 请求前刷新token,避免使用队列保存axios请求配置,减少复杂度
  3. 使用一个全局promise, 避免多接口失效时,refreshToken 多次执行
// axios.ts
import axios, { AxiosError } from 'axios'
import { router } from './App'

let promise = Promise.resolve()

axios.interceptors.request.use(async function (request) {
  await refreshToken()
  const token = localStorage.getItem('token') || ''

  if (request.headers != null) {
    request.headers['token'] = token
  }

  return request
})

function refreshToken() {
// 上一次的 token 更新时间, login | refreshToken
  const lastUpdate = Number(localStorage.getItem('lastUpdate'))

  // 24 小时主动刷新一次token
  if ((lastUpdate && Date.now() - lastUpdate < 1000 * 24 * 60) || !lastUpdate) {
    return
  }
  // 使用一个新的axios实例,避免走到拦截器里导致死循环
  const instance = axios.create({
    baseURL: BASE_URL,
  })
// 请求之前 或者 请求结束(无论成功失败) 更新 lastUpdate 
  localStorage.setItem('lastUpdate', String(Date.now()))
  // 经典的 promise = promise.then 代码, 来控制同一时刻仅有一个refreshToken在执行
  promise = promise.then(() => {
    return instance
      .get('/refreshToken', {
        params: {
          refresh_token: localStorage.getItem('refreshToken'),
        },
      })
      .then(async (res) => {
        localStorage.setItem('token', res.data.data)
        return Promise.resolve()
      })
      .catch((err) => {
        router.navigate('/login')
        return Promise.reject('refreshToken失效,重新登录')
      })
  })
  return promise
}
// App.tsx
import {
  createBrowserRouter,
  RouterProviderProps,
} from 'react-router-dom'

export let router: RouterProviderProps['router']

useEffect(async ()=>{
 const routes = await fetchRoutes()
 router = createBrowserRouter(routes)
},[])

此外,在登录之后,需要执行一下 localStorage.setItem('lastUpdate', String(Date.now()))