Skip to content

token 无感刷新

思路

视频讲解

  1. 准备两个 token:

    1. refresh(长 token):用于刷新 token 使用
    2. token(短 token):用于业务逻辑使用
  2. 在 axios 的相应拦截中统一处理刷新 token 的逻辑,核心如下:

    js
    import axios from 'axios'
    import { getToken, setToken, setRefreshToken } from './token'
    import { refreshToken, isRefreshRequest } from './auth'
    
    const ins = axios.create({
      baseURL: 'https://api.example.com',
      headers: {
        Authorization: `Bearer ${getToken()}`,
      },
    })
    
    // 拦截器
    ins.interceptors.response.use(
      async function (res) {
        // 如果服务器返回新的 token,则更新本地 token
        if (res.headers.authorization) {
          // 如果服务器返回新的 token,则更新本地 token
          const token = res.headers.authorization.replace('Bearer ', '')
          setToken(token)
          ins.defaults.headers.Authorization = `Bearer ${token}`
        }
    
        // 如果服务器返回新的 refreshtoken,则更新本地 refreshtoken
        if (res.headers.refreshtoken) {
          const refreshtoken = res.headers.refreshtoken.replace('Bearer ', '')
          setRefreshToken(refreshtoken)
        }
    
        // 条件1:如果无权限(可能 token 过期,也可能 refresh 过期)
        // 条件2:不是 refreshToken 请求
        if (res.data.code === 401 && !isRefreshRequest(res.config)) {
          // 刷新 token
          const isSuccess = await refreshToken()
          // 是否成功刷新了 token
          if (isSuccess) {
            // 使用新的 token 重新请求
            res.config.headers.Authorization = `Bearer ${getToken()}`
            const res = await ins.request(res.config)
            return res.data
          } else {
            // 无权限
            console.log('回到登录页,重新登录')
            return res.data
          }
        }
    
        // 正常返回数据
        return res.data
      },
      function (error) {
        return Promise.reject(error)
      },
    )
  3. 补充 refresh token 的请求逻辑:

    js
    import request from './request'
    import { getRefreshToken } from './token'
    
    // 防止重复刷新 token
    let promise = null
    
    /** 刷新 token */
    export async function refreshToken() {
      if (promise) {
        return promise
      }
      promise = new Promise(async (resolve) => {
        const res = await request.get('/refresh_token', {
          headers: {
            Authorization: `Bearer ${getRefreshToken()}`,
          },
          _isRefreshToken: true,
        })
        resolve(res.code === 20000)
      })
      promise.finally(() => {
        promise = null
      })
      return promise
    }
    
    /** 是否是刷新 token 的请求 */
    export async function isRefreshRequest(config) {
      return !!config._isRefreshToken
    }

单点登录

什么叫单点登录?

用户在一个应用登录后,可以直接访问其他应用,而不需要再次登录。

例如:用户在应用 A 登录后,可以直接访问应用 B,而不需要再次登录。

常见问题

为什么 token 有效期设置的比较短?

  1. 安全
  2. 控制力

基于 MIT 许可发布