前端无感刷新token

单个接口的刷新token很好解决,难点是多接口并发
首先将token过期后的请求存到一个数组队列中,想办法让这个请求处于等待中,一直等到刷新token后再逐个重试清空请求队列。
那么如何做到让这个请求处于等待中呢?为了解决这个问题,我们得借助Promise。将请求存进队列中后,同时返回一个Promise,让这个Promise一直处于Pending状态(即不调用resolve或reject),此时这个请求就会一直等啊等,只要我们不执行resolve或reject,这个请求就会一直在等待。当刷新请求的接口返回来后,我们再调用resolve或reject,逐个重试
基于nuxt框架,$axios模块,伪代码如下:

import { Message, Loading } from "element-ui"
import Vue from "vue"
export default ({ $axios, store, app, redirect, route, req, res }) => {
  let [loadingCount, currencyCode, currentLanguage, isRefreshing, token] = [
    0,
    "",
    "",
    true,
    ""
  ]
  $axios.onError(error => {
    const code = parseInt(error.response && error.response.status)

    if (error.response && code === 401) {
      if (!token) {
        if (error.response.config.headers.GoLogin) {
          goLogin()
          return
        }
        return
      }

      if (isRefreshing) {
        // 刷新token
        refreshToken(error.response.config.headers.GoLogin)
      }
      isRefreshing = false
      // 将请求挂起(返回一个pending状态的promise,等待刷新接口返回结果后,再调用resolve或reject)
      const retryOriginalRequest = new Promise((resolve, reject) => {
        addSubscriber(token => {
          if (!token) {
            /* eslint-disable */
            return reject("token refresh error")
            /* eslint-enable */
          }
          if (error.response && token) {
            const config = error.response.config
            config.headers.Authorization = `Bearer ${token}`
            $axios.request(config).then(res => {
              resolve(res)
            })
          }
        })
      })
      return retryOriginalRequest
    }
    return Promise.reject(error)
  })

  async function refreshToken(config) {
    let host = ""
    if (process.server) {
      host = store.state.host || ""
    } else {
      host = ""
    }
    const data = await $axios
      .$post(`${host}/account/refresh-token`, {}, { baseURL: "" })
      .catch(err => {
        // 清空store里面的用户信息
        store.commit("setUser", {})
        // reject
        onAccessTokenFetched("")
        if (config) {
          goLogin()
        }
        return Promise.reject(err)
      })
    let expireIn = ""
    if (data && data.code === 0) {
      token = data.data.token
      expireIn = data.data.expires_in
      if (process.server) {
        const stringObject = req.headers.cookie
        req.headers.cookie = replaceParamVal(stringObject, "token", token)

        res.setHeader(
          "Set-Cookie",
          `token=${token};Domain=.heavengifts.com;Path=/;Max-Age=${expireIn}`
        )
      }
      // 刷新token成功,执行数组里的函数,重新发起被挂起的请求(resolve)
      onAccessTokenFetched(token)
    } else {
      // 清空store里面的用户信息
      store.commit("setUser", {})
      // reject
      onAccessTokenFetched("")
      if (config) {
        goLogin()
      }
    }
    // app.$cookies.set("token", token)
    isRefreshing = true
  }

  // 替换新token
  function replaceParamVal(stringObject, paramName, replaceWith) {
    const str = stringObject.replace(/\s/g, "")
    /* eslint-disable */
    const re = eval('/('+ paramName+'=)([^;]*)/gi')
    /* eslint-enable */
    const newParam = str.replace(re, paramName + "=" + replaceWith)
    return newParam
  }

  // 被挂起的请求数组
  let subscribers = []

  // push所有请求到数组中
  function addSubscriber(callback) {
    subscribers.push(callback)
  }

  // 刷新请求(subscribers数组中的请求得到新的token之后会自执行,用新的token去请求数据)
  function onAccessTokenFetched(token) {
    subscribers.forEach(callback => {
      callback(token)
    })
    subscribers = []
  }

  function goLogin() {
    if (process.server) {
      redirect(`/account/login`)
    } else {
      window.location.href = `/account/login`
    }
  }
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这道题涉及到前端中的认证和授权问题,以及如何在前端中处理 token 刷新的问题。 认证和授权是指用户在访问系统资源时需要进行身份验证和权限验证。前端通常会在用户登录后返回一个 token,然后在每次请求时将 token 带上,服务端会通过 token 来判断用户是否有权限访问资源。 在使用 token 进行认证和授权时,由于 token 有一定的有效期限制,因此需要在 token 过期前进行刷新。在前端中可以通过定时器来定时检查 token 的有效期,当 token 即将过期时,发送一个刷新 token 的请求,获取新的 token,然后将新的 token 存储在本地,同时更新请求头中的 token。 以下是一个示例代码: ```javascript // 定义定时器,每隔一段时间检查 token 是否即将过期 let timer = setInterval(() => { let token = localStorage.getItem('token') let expiredTime = localStorage.getItem('expiredTime') if (new Date().getTime() > expiredTime - 60000) { // token 即将过期 refreshToken(token) } }, 1000) // 刷新 token 的函数 function refreshToken(token) { // 发送请求获取新的 token axios.post('/refreshToken', {token: token}) .then(res => { // 更新本地存储的 token 和过期时间 localStorage.setItem('token', res.data.token) localStorage.setItem('expiredTime', new Date().getTime() + res.data.expiresIn * 1000) // 更新请求头中的 token axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token }) } ``` 需要注意的是,当用户退出登录时,需要及时清空本地存储的 token 和过期时间,并停止定时器。 ```javascript function logout() { localStorage.removeItem('token') localStorage.removeItem('expiredTime') clearInterval(timer) } ``` 总之,前端刷新 token 的主要思路就是定时检查 token 的有效期,当即将过期时发送一个刷新 token 的请求,获取新的 token,然后更新本地存储的 token 和过期时间,并更新请求头中的 token

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值