关于token:实现无感刷新token我是这样做的

65次阅读

共计 2736 个字符,预计需要花费 7 分钟才能阅读完成。

前言

最近在做需要的时候, 波及到登录 token, 产品提出一个问题: 能不能让 token 过期工夫长一点,我频繁的要去登录。

前端:后端,你能不能把 token 过期工夫设置的长一点。

后端:能够, 然而那样做不平安,你能够用更好的办法。

前端:什么办法?

后端:给你刷新 token 的接口,定时去刷新 token

前端:好,让我思考一下

需要

当 token 过期的时候,刷新 token, 前端须要做到无感刷新 token, 即刷 token 时要做到用户无感知,防止频繁登录。实现思路

办法一
后端返回过期工夫,前端判断 token 过期工夫, 去调用刷新 token 接口

毛病:须要后端额定提供一个 token 过期工夫的字段;应用了本地工夫判断,若本地工夫被篡改,特地是本地工夫比服务器工夫慢时,拦挡会失败。

办法二
写个定时器,定时刷新 token 接口

毛病:浪费资源, 耗费性能, 不倡议采纳。

办法三
在响应拦截器中拦挡,判断 token 返回过期后,调用刷新 token 接口

实现

axios 的根本骨架, 利用 service.interceptors.response 进行拦挡

import axios from 'axios'

service.interceptors.response.use(
  response => {if (response.data.code === 409) {return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken()}).then(res => {const { token} = res.data
          setToken(token)
          response.headers.Authorization = `${token}`
        }).catch(err => {removeToken()
          router.push('/login')
          return Promise.reject(err)
        })
    }
    return response && response.data
  },
  (error) => {Message.error(error.response.data.msg)
    return Promise.reject(error)
  })
复制代码

问题解决

问题一:如何避免屡次刷新 token
咱们通过一个变量 isRefreshing 去管制是否在刷新 token 的状态。

import axios from 'axios'

service.interceptors.response.use(
  response => {if (response.data.code === 409) {if (!isRefreshing) {
        isRefreshing = true
        return refreshToken({refreshToken: localStorage.getItem('refreshToken'), token: getToken()}).then(res => {const { token} = res.data
          setToken(token)
          response.headers.Authorization = `${token}`
        }).catch(err => {removeToken()
          router.push('/login')
          return Promise.reject(err)
        }).finally(() => {isRefreshing = false})
      }
    }
    return response && response.data
  },
  (error) => {Message.error(error.response.data.msg)
    return Promise.reject(error)
  })
复制代码

问题二:同时发动两个或者两个以上的申请时,其余接口怎么解决
当第二个过期的申请进来,token 正在刷新,咱们先将这个申请存到一个数组队列中,想方法让这个申请处于期待中,始终等到刷新 token 后再一一重试清空申请队列。那么如何做到让这个申请处于期待中呢?为了解决这个问题,咱们得借助 Promise。将申请存进队列中后,同时返回一个 Promise,让这个 Promise 始终处于 Pending 状态(即不调用 resolve),此时这个申请就会始终等啊等,只有咱们不执行 resolve,这个申请就会始终在期待。当刷新申请的接口返回来后,咱们再调用 resolve,一一重试。最终代码:

import axios from 'axios'

// 是否正在刷新的标记
let isRefreshing = false
// 重试队列
let requests = []
service.interceptors.response.use(
  response => {
  // 约定 code 409 token 过期
    if (response.data.code === 409) {if (!isRefreshing) {
        isRefreshing = true
        // 调用刷新 token 的接口
        return refreshToken({refreshToken: localStorage.getItem('refreshToken'), token: getToken()}).then(res => {const { token} = res.data
          // 替换 token
          setToken(token)
          response.headers.Authorization = `${token}`
        }).catch(err => {
        // 跳到登录页
          removeToken()
          router.push('/login')
          return Promise.reject(err)
        }).finally(() => {isRefreshing = false})
      } else {
        // 返回未执行 resolve 的 Promise
        return new Promise(resolve => {
          // 用函数模式将 resolve 存入,期待刷新后再执行
          requests.push(token => {response.headers.Authorization = `${token}`
            resolve(service(response.config))
          })
        })
      }
    }
    return response && response.data
  },
  (error) => {Message.error(error.response.data.msg)
    return Promise.reject(error)
  })
复制代码

总结
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163 互相学习,咱们会有业余的技术答疑解惑

如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点 star: http://github.crmeb.net/u/defu 不胜感激!

正文完
 0