1- 实现背景
之前实现token刷新的方案是在项目运行或者刷新的时候设置一个固定时间的定时器,然后定时器里面是请求刷新token的接口并重新调用当前定时器方法。此方式看似没有问题,实则存在不能覆盖的token时间。 Ep:设定token过期时间为5min。我们在登陆成功时获取到了token,设置了一个4.5min定时刷新的定时器。3min以后我们执行一次刷新操作,此时整个项目都会重新运行,那么4.5min的倒计时也会重新计算。但此时距离token过期时间仅剩1.5min了,而刷新token操作的定时器还需要4.5min才会执行。在这4.5min-1.5min=3min的时间里只要有请求发送,都会返回401进行重新登录了。
2- 解决思路
上诉方案的主要问题是定时器是写死的,所以面对不固定的刷新操作的时候就无法应对。解决方案可以效仿cookie,增加一个expries参数,就可以随时获取到token的过期时间,这样无论是登陆成功还是执行刷新操作,我们都可以根据expries参数设置一个可调节时间的定时器,就不会出现无法覆盖的问题。 Ep: 设定token过期时间为5min。我们保存token以及当前的时间戳,并设置一个时间戳-30s的定时器,这样在token过期30s之前就会之执行刷新token。当执行刷新操作的时候,重复上述步骤,就可以实现定时器的倒计时是可以调节的了。
3- 具体实现
localStorage是仅支持字符串的,并不支持添加附属的属性,所以我们需要手动封装一下。
import api from "@/utils/request.js"
import { message } from "antd"
// 保存token
export function saveToken(token) {
// 服务器5分钟过期 本地存储为4.5分钟后过期
const expries = new Date().getTime() + 4.5 * 60 * 1000
const obj = { token, expries }
window.sessionStorage.setItem("informationToken", JSON.stringify(obj))
}
// 删除token
export function delToken() {
window.sessionStorage.removeItem("informationToken")
}
// 查询token
export function getToken() {
const obj = JSON.parse(window.sessionStorage.getItem("informationToken"))
return obj ? obj.token : ""
}
// 刷新token
export async function refreshToken() {
// 判读过期时间
const { expries } = JSON.parse(
window.sessionStorage.getItem("informationToken")
)
const isExpries = expries - new Date().getTime()
console.log(`🚀 ~ 距离刷新token时间 还有${isExpries / 1000}秒`)
if (isExpries > 0) {
setTimeout(async () => {
const res = await api.post("/login/fresh")
saveToken(res.token)
refreshToken()
}, isExpries)
} else {
message.error("您的登陆认证已过期 请重新登陆!")
setTimeout(() => {
window.location.href = "/login"
}, 3 * 1000)
}
}
over