编程崽

登录

一叶在编程苦海沉沦的扁舟之上,我是那只激情自射的崽

刷新token队列管理

刷新token队列管理

这种处理可以应用在多种场景,这里拿刷新token举例。

场景:

用户正在使用后台管理系统,已经登录了,当用户打开某个页面时,恰好token过期。

那么此后访问的接口,都会返回【登录已过期】的标记,前台需要静默的自动去刷新token,token刷新成功后,直接再次调用接口。

但有时情况比较特殊,比如某个页面一打开,需要同时调用5个接口甚至更多,由于这些接口几乎都是同时调用的,那这些接口都会收到【登录已过期】的标记,那每个接口都会去调用刷新token的接口。

这就导致,查看控制台的network(网络),会发现5个接口都失败了,同时瞬间出现了5个刷新token的接口,后面就知道了,token一瞬间被刷新了5次,很不优雅。

解决:

所以,这里添加了一个缓存控制器,原理就是,使用额外一个字段和一个数组。

  • 字段用来标记当前是否是正在进行刷新token的操作,用来保证同时只有一个刷新token的接口在调用。
  • 数组用来存放Promise的resolve,每当调用刷新token操作时,都把resolve放入数组中,当刷新token的操作完成时,依次弹出并执行这些resolve。

封装refreshToken方法:

ts 复制代码
// 刷新token方法
// 为了处理同时多个接口要刷新token的控制器
const refreshTokenCache = {
  // 是否正在刷新token
  isRefreshToken: false as boolean,
  // 当正在进行的token刷新时,后来的请求都存放在这个数组中,刷新token后再执行,传入token是否刷新成功
  cbList: [] as ((isSuccess: boolean) => void)[],
}

// 供外部调用的刷新token的方法
async function refreshToken(): Promise<boolean> {
  return new Promise<boolean>(async resolve => {
    // 直接把promise的resolve推入缓存数组中,等token刷新完成后,会依次执行
    refreshTokenCache.cbList.push(resolve)
    // 发现正在进行刷新token,直接返回
    if (refreshTokenCache.isRefreshToken) return
    // 设定为正在进行刷新token
    refreshTokenCache.isRefreshToken = true

    // 刷新token是否成功
    let isSuccess = false
    try {
      // ...调用刷新token的接口
      isSuccess = true
    } catch (error: any) {
      isSuccess = false
    }

    // 处理所有等待刷新token完后的方法
    while (refreshTokenCache.cbList.length) {
      // 这里每一个cb方法,都是一个resolve,去执行就行
      let cb = refreshTokenCache.cbList.shift()
      // 把token刷新的结果传入
      cb?.(isSuccess)
    }
    // 设定为没有在刷新token
    refreshTokenCache.isRefreshToken = false

    // 刷新token失败,进行错误处理
    if (!isSuccess) {
      // 一般可以在这里弹一个登录过期请重新登录的提示
      // 然后清除token、用户数据等,然后跳转到登录页
    }
  })
}

使用refreshToken的示例:

ts 复制代码
// 封装的接口请求后,处理响应状态、参数的拦截方法
async requestHandler<T = any>(axiosResponse: AxiosResponse) {
  // 比如和后台约定,当状态值是424时,是登录过期
  if (axiosResponse?.status === 424) {
    // token过期,自动去刷新token
    const success = await refreshToken()
    // 注意,这里如果刷新token成功,会重新发起请求,所以这里需要递归调用
    if (success) return this.request(axiosResponse?.config)
    // 刷新token失败,直接返回空数据
    // 这里不进行错误提示,因为如果有多个接口在同时刷新token,会造成多个错误提示
    // 所以错误提示在 refreshToken 中处理,那里有管理器,可以保证只报错一次
    return null
  } else if (axiosResponse?.status !== 200) {
    // 其他错误
  }
  // ...其他处理

  // 接口正常,返回解析后的接口响应接口
  return axiosResponse.data
}
更新时间:2024/04/02 14:07:03