Чтобы получить бессмысленный токен обновления, я сделал это

внешний интерфейс Vue.js
Чтобы получить бессмысленный токен обновления, я сделал это

Оригинальная ссылка Добавить Автора

предисловие

Когда я недавно делал запросы, когда дело дошло до входа в токены, продукт поднял вопрос: можно ли увеличить срок действия токена, мне приходится часто входить в систему.

Front-end: Back-end, можете ли вы увеличить время истечения срока действия токена.

Backend: Да, но это небезопасно, вы можете использовать лучший способ.

Передняя часть: какой метод?

Серверная часть: дает вам интерфейс для обновления токена и регулярно обновляет токен.

Передняя часть: Хорошо, дайте мне подумать об этом

нужно

Когда срок действия токена истекает, обновите токен, а интерфейс должен обновить токен, не чувствуя, то есть, когда токен смахивается, пользователь должен быть без сознания и избегать частых входов в систему. Реализовать идеи

метод первый

Серверная часть возвращает время истечения срока действия, а внешний интерфейс оценивает время истечения срока действия токена и вызывает интерфейс токена обновления.

Недостатки: Бэкенд должен предоставить дополнительное поле времени истечения срока действия токена; используя оценку локального времени, если местное время подделано, особенно когда местное время медленнее, чем время сервера, перехват не удастся.

Способ второй

Напишите таймер для регулярного обновления интерфейса токена

Недостатки: пустая трата ресурсов, потребление производительности, не рекомендуется.

Способ третий

Перехват в перехватчике ответа и вызов интерфейса токена обновления после определения того, что срок возврата токена истек.

выполнить

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)
  })

задача решена

Одна проблема: как предотвратить многократный токен обновления

Мы используем переменную isRefreshing, чтобы контролировать, обновляется ли состояние токена.

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)
  })

Вопрос 2: Как решить другие интерфейсы, когда два или более запросов инициируются одновременно

Когда приходит второй запрос с истекшим сроком действия и токен обновляется, мы сначала сохраняем запрос в очереди массива и пытаемся удержать запрос в ожидании, пока токен не будет обновлен, а затем повторяем попытку очистить очередь запросов один за другим. Итак, как сделать этот запрос ожидающим? Чтобы решить эту проблему, мы должны прибегнуть к промисам. После сохранения запроса в очереди одновременно возвращается промис, так что промис всегда находится в состоянии Pending (то есть разрешение не вызывается).В это время запрос будет ждать и ждать, пока мы не выполним разрешение, запрос всегда будет в ожидании. Когда интерфейс запроса на обновление возвращается, мы вызываем разрешение и повторяем попытку один за другим. Окончательный код:

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}`
           // token 刷新后将数组的方法重新执行
          requests.forEach((cb) => cb(token))
          requests = [] // 重新请求完清空
          return service(response.config)
        }).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)
  }
)