前端axios实现登入过期token拦截刷新

不少管理系统都使用了token机制,虽然token有不少优点。但缺点也明显,例如: 如果token我们后台签发了,但token给盗用了,常规情况下我们没办法让token主动过期,如果要想让token主动过期,我们得配合redis中间件。

token如果设置过期时间太短,安全性提高了,但用户体验下降了,你也不想在入录表单的时候数据填好了,一保存就给我跳转到登入页面吧,这种情况相当炸裂,之前的操作都要重新来一遍了。但如果token设置时间太长,系统安全又大大降低。那有没有一种更好的方式来提高t系统的安全性和使用体验呢。

token刷新极其过期请求重新发送,具体实现如下:

新建refresh-token.ts文件,代码如下:

import { AxiosResponse } from 'axios'
import { refreshToken } from '@/api/user/admin-login'
import router from '@/router'

import { setToken, clearToken } from '@/utils/common/auth'

let updateTokenTag = true,
  requestArr: any[] = []

/**
 * 刷新token
 * @param response
 * @param axios
 * @returns
 */
export const refreshTokenHandle = async (
  response: AxiosResponse,
  axios: any
) => {
  const config = response.config
  if (updateTokenTag) {
    console.log('正在刷新短token')
    updateTokenTag = false
    const config = response.config
    //获取刷新token
    const token = localStorage.getItem('refreshToken') as string
    try {
      //刷新token
      const res = await refreshToken(token)
      if (res.code === 200) {
        //设置token
        setToken(res.data.accessToken, res.data.refreshToken)
        // 执行完更新操作,重新执行未执行请求
        requestArr.forEach((execute) => execute())
        // 置空队列数组
        requestArr = []
        config.headers['satoken'] = res.data.accessToken

        console.log('token刷新成功')

        return axios(config)
      } else {
        console.log('token刷新失败')
        requestArr = []
        clearToken()
        router.push('/cvmagic/login')
      }
      // 这里是第一次执行进入更新token的接口继续往下执行
    } catch {
      clearToken()
      router.push('/cvmagic/login')
    } finally {
      updateTokenTag = true
    }
  } else {
    // 通过异步将并行的接口加入队列中,等上面更新完token接口再执行队列
    return new Promise((resolve) => {
      // 用函数的方式将相应数据resolve出去,执行函数就能得到响应结果
      requestArr.push(() => {
        return resolve(axios(config))
      })
    })
  }
  return response
}

这有俩个难点

一.是如何防止token刷新接口重复执行。

解决:我们设置updateTokenTag开关判断当前是否进入token刷新状态,进入后把状态设置为false,当token刷新结束设置会true

二.如何让token过期的请求重新执行。

解决:定义一些数组,把其他的请求放入数组中,等待token刷新完毕后再重新执行。

接下来我们就前端双token实现。axios二次封封装,导入refresh-token.ts在axios响应拦截器处理刷新token

import axios from 'axios'
import type { AxiosInstance } from 'axios'
import * as message from './message'
import { codeEnum } from '@/enums/codeEnum'
import { refreshToken } from '@/api/user/admin-login'
import router from '@/router'

import { refreshTokenHandle } from './refresh-token'

const http: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 5000,
})

//重试次数
const count = 5
//重试计数
let num = 1

// 请求前拦截
http.interceptors.request.use(
  (config) => {
    // const token = JSON.parse(localStorage.getItem('userinfo')).token
    config.headers['satoken'] = localStorage.getItem('satoken')
    return config
  },
  (err) => {
    console.log(err)
  }
)

// 请求数据返回前拦截
http.interceptors.response.use(async (response) => {
  console.log(response.data, response.config.url)
  message.msg(response.data.code, response.data.msg)

  if (response.data.code === 401) {
    return refreshTokenHandle(response, axios)
  }

  return response
})

export const get = <T = unknown>(...args: any): Promise<T> => {
  const [url, params] = args
  return new Promise((resolve, reject) => {
    http
      .get(url, { params })
      .then((res) => {
        resolve(res.data)
      })
      .catch(() => {
        if (num <= count) {
          console.log(`重试次数:${num}`)
          num++
          get(url, params)
        } else {
          reject(url + 'get请求失败')
          return
        }
      })
  })
}

export const post = <T = unknown>(...args: any): Promise<T> => {
  const [url, params, headers] = args

  return new Promise((resolve, reject) => {
    http
      .post(url, params, headers)
      .then((res) => {
        resolve(res.data)
      })
      .catch((err) => {
        reject({
          err,
          msg: 'post请求失败',
        })
      })
  })
}

export const del = <T = unknown>(...args: any): Promise<T> => {
  const [url, data, headers] = args
  return new Promise((resolve, reject) => {
    console.log(url)

    http
      .delete(url, {
        data,
      })
      .then((res) => {
        resolve(res.data)
      })
      .catch((err) => {
        reject({
          err,
          msg: 'delete请求失败',
        })
      })
  })
}

export const put = <T = unknown>(...args: any): Promise<T> => {
  const [url, params, headers] = args

  return new Promise((resolve, reject) => {
    console.log(url)
    http
      .put(url, params, headers)
      .then((res) => {
        resolve(res.data)
      })
      .catch((err) => {
        reject({
          err,
          msg: 'put请求失败',
        })
      })
  })
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
无感刷新token是指在前端发起请求时,如果当前的token过期,则自动刷新token,并重新发起请求,达到用户无感知的效果。在axios中,可以通过拦截实现无感刷新token的功能。 在axios中,我们可以通过使用请求拦截器和响应拦截器来实现无感刷新token的逻辑。首先,我们可以在请求拦截器中判断当前的token是否已过期。如果token过期,则直接返回请求对象,如果token过期,则在请求拦截器中发起刷新token的请求。 在刷新token的请求中,我们可以使用当前过期token去请求后端接口,然后根据后端返回的结果来判断是否成功刷新token。如果刷新token成功,则将新的token更新到请求头信息中,并重新发起之前的请求。 在响应拦截器中,我们可以判断后端返回的响应状态码。如果响应状态码为401,说明当前的token过期,则调用刷新token的逻辑重新获取token,并再次发起请求。如果刷新token的请求失败,则说明用户的登录状态无效,可以跳转到登录页面重新登录。 通过以上的逻辑,我们可以实现无感刷新token的效果,用户不需要手动处理token过期的问题,从而提升用户体验。同时在后端也需要配合实现刷新token的逻辑,并提供相应的接口供前端调用。 无感刷新token实现涉及到了前后端的配合,前端通过axios拦截实现token的自动刷新,后端提供相关的接口。通过这种方式,可以有效解决token过期后需要重新登录的问题,提升应用的用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值