前端axios实现登入过期token拦截刷新
token如果设置过期时间太短,安全性提高了,但用户体验下降了,你也不想在入录表单的时候数据填好了,一保存就给我跳转到登入页面吧,这种情况相当炸裂,之前的操作都要重新来一遍了。不少管理系统都使用了token机制,虽然token有不少优点。但缺点也明显,例如: 如果token我们后台签发了,给盗用了,常规情况下我们没办法让token主动过期,如果要想让token主动过期,我们得配合redis中间件。
不少管理系统都使用了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请求失败',
})
})
})
}
更多推荐
所有评论(0)