前端无痛刷新Token

前言

记得上年大三期间,受同学邀请一起重写学校的一个实验系统,当时真的是感觉啥也不会(现在会的也不多,哈哈)。在我们几个进行讨论的时候就说了这个需求。这个系统主要功能是学生们进行提交一些表单操作,所以这个需求挺关键的。但当时因自身技术不行,就略过了这个功能。今年也是刚步入实习阶段,在阅读公司以前项目的时候就发现了无感刷新Token的封装痕迹,但只是一些雏形,就是函数封装了一半,不知道什么原因最后也没有使用。
决定使用自己仅有的技术重新梳理一下
无痛刷新Token的需求场景很多,例如:用户正在提交一个复杂的表单,突然拉肚子取了个厕所,回来点击提交表单发现Token失效,强制返回了登录页。我要是当时的用户,我可能会pen死开发的,哈哈,这种用户体验感很差。所以说前端实现无痛刷新Token是非常有必要的。


一、方案

  • 方案一
    后端返回过期时间,前端每次请求就判断token的过期时间,如果快到过期时间,就去调用刷新token接口。
    缺点:需要后端额外提供一个token过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败。
  • 方案二
    写个定时器,定时刷新token接口。
    缺点:浪费资源,消耗性能,不建议采用。
  • 方案三
    在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口。

综合上面的三个方法,最好的是第三个,因为它不需要占用额外的资源。
接下来,我们就分析一下第三种的实现方式

二、分析

1.创建一个数组队列pendingQueue存放需要重新发起的请求
2. 创建一个flag ifRefreshPending来判断是否刷新中
3. 在请求拦截器中将请求添加到pendingQueue
4. 在响应拦截器中判断Token是否过期:
    1.Token没有过期,将pendingQueue中的本次请求移除,正常返回响应数据
    2.Token过期,刷新TokenifRefreshPending判断是否正在刷新中,防止重复刷新TokenToken刷新
成功,将pendingQueue存放的请求重新请求一次,然后清空pendingQueue

三、简易封装

// 存储pending状态下的请求
const pendingQueue = new Map()
// 是否正在刷新的标志
let ifRefreshPending = false
// 刷新Token的接口函数
const refreshToken = () => {
    return new Promise((resolve, reject) => {
        // 通过接口获取新的token
		// ...	
        resolve(token)
    })
}
// 添加请求
const addPending = config => {
    const {url, method, data} = config
    if(!pendingQueue.has(url)){
        // 将请求添加到task
        pendingQueue.set(url,{
            url,
            method,
            data
        })
    }
}
// 移除请求
const removePending = config =>{
    pendingQueue.delete(config.url)
}
axios.interceptors.request.use(config =>{
	removePending(config)  // 再添加之前移除之前的本次请求
    addPending(config)
    // 添加Token
    config.headers.Authorization = localStorage.getItem('token')
    return config
})
  • 假设和后端约定Token过期返回状态码 code = 0
axios.interceptors.response.use(response =>{
    if(response.code === 0){
        // 说明整在刷新Token
        ifRefreshPending = true
        if(!ifRefreshPending){
            // TODO: 刷新Token, 将pendingTask中存储的请求重新执行
            return refreshToken().then(res => {
                localStorage.setItem('token', res.token)
                // 将pendingQueue中存储的请求重新执行
                pendingQueue.forEach(item => {
					// TODO 重新发起请求	
				})
				// 清空pendingQueue
				pendingQueue.clear()
            }).catch(err=>{
                // 刷新Token失败就跳到登录页
                router.push('/login')
                return Promise.reject(err)
            }).finally(() => {
                ifRefreshPending = false
            })
        }
    }
    // 正常响应就移除本次请求
    removePending(response.config)
    return response && response.data
})

总结

技术不精,谅!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值