关于token无痛刷新的两个方案

方案一

思路:

后端需要返回一个token过期的时间,在请求发起前拦截每个请求,判断token的有效时间是否已经过期,如果已过期,则暂时将请求挂起,先刷新token后再继续请求。

前期思考:

  1. 返回过期时间是为了判断是否过期的,但是如果服务器时间和系统时间不一致呢?
  2. 如果同时发起多个请求时怎么办?

实现:
因为是在请求前进行拦截,所以这里用axios的axios.interceptors.request.use()方法

首先,关于服务器时间和系统时间不一致的问题,我的解决方法是:

后端返回的过期时间应该是一个时间段,而不是绝对时间,这样就避免了二者时间不一致的问题。前端拿到token的有效时间后,在存到localStorage前,先依据系统时间转成绝对时间。
例如:当前时间是12点,有效时间是3600秒(1个小时),则存到localStorage的过期时间是13点的时间戳,使用时只需要判断这个绝对时间即可。

  1. 先不考虑复杂情况,正常接口请求
// 请求前先进行拦截
instance.interceptors.request.use((config) => {
	//这里不单单是获取到token,还要一个token的过期时间,用来进行下面的判断
	const tokenInfo = getToken()
	/**
	 * 这里模拟一下tokenInfo的结构
	 * tokenInfo:{
	 * 		token:'fdasfeffafdsfasfdasfdas',
	 * 		tokenExpireTime:'36000'
	 * }
	 */
	//这里先拿到token,给每个请求添加一个token请求头
	  config.headers['token'] = tokenInfo.token
 
	// 注意:登录接口和刷新token接口是不需要token的
	  if (config.url.indexOf('/refreshToken') >= 0 || config.url.indexOf('/login') >= 0) {
	    return config
	  }
 
	//这里是拦截的关键代码
	if (tokenInfo.token && tokenInfo.tokenExpireTime) {
	      const now = Date.now()
	      if (now >= tokenInfo.tokenExpireTime) { // 这里已经过期了。
	          //返回一个Promise,执行refreshToken后再return当前的config
	          
	          return refreshToken().then(res => {
	            const { token, tokenExprieIn } = res.data
	            //重新把时间段转成绝对时间存起来
	            const tokenExpireTime = now + tokenExprieIn * 1000  
	            instance.setToken({ token, tokenExpireTime }) // 存token到localStorage
	            config.headers['token'] = token // 请求头重新赋值token
	            return config
	          }).catch(res => {
	            console.error('token刷新error', res)
	          })
	      }
	  }
 	return config
}, (error) => {
  return Promise.reject(error)
})
  1. 多个请求同时发起
    当同时发起多个请求,势必会多次调用refreshToken(),这时只需要就需要一个isFlag的状态来判断是否正在刷新token
let isFlag = false
instance.interceptors.request.use((config) => {
   //这里不单单是获取到token,还要一个token的过期时间,用来进行下面的判断
   const tokenInfo = getToken()
   /**
    * 这里模拟一下tokenInfo的结构
    * tokenInfo:{
    * 		token:'fdasfeffafdsfasfdasfdas',
    * 		tokenExpireTime:'36000'
    * }
    */
   //这里先拿到token,给每个请求添加一个token请求头
     config.headers['token'] = tokenInfo.token
     
   // 注意:登录接口和刷新token接口是不需要token的
     if (config.url.indexOf('/refreshToken') >= 0 || config.url.indexOf('/login') >= 0) {
       return config
     }
     
   //这里是拦截的关键代码
   if (tokenInfo.token && tokenInfo.tokenExpireTime) {
         const now = Date.now()
         if (now >= tokenInfo.tokenExpireTime) { // 这里已经过期了。
             //返回一个Promise,执行refreshToken后再return当前的config
             
             if (!isRefreshing) {
               	isFlag = true
               	return refreshToken().then(res => {
   	            	const { token, tokenExprieIn } = res.data
   	            	//重新把时间段转成绝对时间存起来
   	            	const tokenExpireTime = now + tokenExprieIn * 1000  
   	            	instance.setToken({ token, tokenExpireTime }) // 存token到localStorage
   	            	isFlag = false //一旦刷新成功,再重置回去
   	            	config.headers['token'] = token // 请求头重新赋值token
   	            	return config
   	        	}).catch(res => {
   	            	console.error('token刷新error', res)
   	        	}) 
             }
         }
     }
     return config
}, (error) => {
 return Promise.reject(error)
})

通过isFlag打断了多次请求后,需要用到一个Promise来让所有请求挂起,这里我们把其他请求先存到一个数组里等待token刷新完来请求他。
这里我们再加一个.then来一步一步进行

 if (!isRefreshing) {
   	isFlag = true
   	return refreshToken().then(res => {
    	const { token, tokenExprieIn } = res.data
    	//重新把时间段转成绝对时间存起来
    	const tokenExpireTime = now + tokenExprieIn * 1000  
    	instance.setToken({ token, tokenExpireTime }) // 存token到localStorage
    	isFlag = false //一旦刷新成功,再重置回去
    	config.headers['token'] = token // 请求头重新赋值token
    	return config
	}).then((token) => {
      console.log('刷新token成功,执行队列')
      //在token刷新成功后,才会继续执行队列
      requestsArr.forEach(item => item(token))
      // 执行完成后,清空队列
      requestsArr = []
    }).catch(res => {
		console.error('token刷新error', res)
	}) 
 }else{ //但是这样写还是会出问题,多次发起请求的顺序会乱,所以这边加个else
 	//这里第一个请求进不来
    const retryOriginalRequest = new Promise((resolve) => {
      requestsArr.push((token) => {
        config.headers['token'] = token
        resolve(config)
      })
    })
    return retryOriginalRequest
  }

方案二

思路:

先不管token是否过期,直接请求,如果返回401(后端定义的,我这里401是token过期),去刷新token,再重新调之前的接口

我个人并不推荐这种方法,先不更新这个了。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值