关于refresh_token

前文介绍过jwt的一般使用场景,用户登录成功后获得jwt,其中包含用户相关信息,主要是在前端要用到的属性(比如姓名、应用角色[这个前端后都用得着]等)、在后端要用到的属性(比如登录IP、终端唯一标识)、token过期时间(这个前后端都可以检查),之后调用后端服务都会在header里带上jwt,具体就是Authorization属性的值设置为’Bearer ’ + jwt。这样后端收到请求后,会对jwt进行验签和解析,如果签名正确,且在有效期内,则认可jwt解析出来的用户身份,进行相关后续处理,否则跳转登录界面重新登录。
出于安全考虑,再加上用户身份权限也不是一成不变的,所以一般token有效期不应设置过长,比如一个小时?其实不论多长时间,总会有超期时间,这时候用户访问时会跳转到登录界面要求重新登录,这样的体验是不太好的,所以一般情况下,登录成功后,可以提供两个token给前端,一个是access_token,也就是前面介绍的通常意义上的jwt,而另一个是refresh_token,它的有效期比access_token的要长一些,用户在access_token过期且access_token未过期情况下为前端重新分配access_token(其实也可以同时更新refresh_token)
我们可以将refresh_token放在access_token的属性中,这样每次access_token传到后端时,refresh_token也自然一起传了过来,当然也可以单独放在header里专门建一个属性每次传后端也是可以的。
其实有两种方式来执行token的更新,一种是利用了后端主动访问前端的SSE或者websocket方式,后端解析请求发现access_token过期且refresh_token未过期,可以主动从SSE或websocket通道连接前端,推送新的access_token和refresh_token到前端,前端收到token更新后存入本地存储,并在每次发起后端请求时放在header相关属性中一并发出。这种方式有违http服务无状态的初衷,虽然可以实现,不过不推荐。
另外一种是在前端axios里配置拦截器,可以配置请求拦截或者响应拦截。区别如下:请求拦截的话,每次发起请求前检查access_token是否超期,如果没有,直接发起原请求,否则检查refresh_token是否超期,如果超期,则跳登录界面,还在有效期的话,可以发起刷新token的调用更新token。返回后写入token到本地,然后放在header里发起原请求,另外很难要求前后端时间严格同步,所以建议在access_token到期前一段时间内比如(一分钟)就请求刷新。配置响应拦截器的话,请求后根据服务器响应再做后续处理,如果响应正常,则原响应返回,如果提示token超期的话,要执行刷新token,返回后写入token到本地,然后放在header里发起原请求。两种拦截器效果区别不大,不过按javascript风格——先操作再处理error——的话,是倾向于响应拦截器的。
这里还有一个小问题,就是在本地存储的token过期,而新的token尚未返回写入本地存储时,可能发起了多个请求,如果后端每次都新生成token就没有必要了,后端可以考虑维护一个列表,存有新生成的token、有效期与前端ip或者唯一标识,这样只要在列表中找到就不用重新生成,直接分配即可。此时前端处理会比较简单,会发起多次申请和写入,不过写入的是同样的内容,其实也没有啥不太好的影响,主要是多次申请token和写回本地存储有些前后端的多余的不必要的开销。改进的方式是在前端设置一个变量来标识是否处于刷新token中,这样第一个刷新会向后端提交申请,而其余的返回token过期的申请或者新的请求申请要暂时挂起放入队列不提交,等待这第一个刷新完成后,队列里申请再激活提交。

var inRefresh = false;
var reqlist = [];

const instance = axios.create();
instance.interceptors.request.use(config=>{
	if (!inRefresh) return config;
	return new Promise(resolve => {
		reqlist.push(token=>{
			config.headers.Authorization = "Bearer "+token;
			resolve(config);
			})
		})
});

instance.interceptors.response.use( response => { return response },  error => {
	if (error.response.status == 401) {
		let config = error.config;
		if (!inRefresh) {
			inRefresh = true;
			axios.post("/app/refresh_token",{"refresh_token":localStorage.getItem('refresh_token')}).then(res=>{
				let jwt=res.data.access_token;
				config.defaults.headers.common['Authorization'] = "Bearer " + jwt;
				localStorage.setItem("jwt", jwt);
				localStorage.setItem("refresh_token", res.data.refresh_token);
				reqlist.forEach((cb) => cb(jwt));
				reqlist = [];
				return instance(config);
				}).catch(err => { return Promise.reject(err) }).finally(() => { inRefresh = false })
			}
		else {
			return new Promise(resolve => {
				reqlist.push(token => {
					config.headers.Authorization = "Bearer "+token;
					resolve(instance(config));
					})
				})
			}
		}
	else return Promise.reject(error)
})
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
refresh_token是一种用于刷新访问令牌(access_token)的凭证。当access_token过期时,可以使用refresh_token来获取新的access_token,以保持用户的登录状态。根据引用\[1\]和引用\[2\]的内容,refresh_token有三个时间点需要考虑:1) 当tokenrefresh_token都没有失效时,可以正常请求;2) 当token失效但refresh_token没有失效时,需要调用api_refresh_token的请求来获取新的token;3) 当tokenrefresh_token都失效时,需要提示token失效,并且前端需要调用api_refresh_token的请求来获取新的token。根据引用\[3\]的内容,前端在拿到新的access_tokenrefresh_token后,可以将存在cookies中的两个token进行更新,并使用新的access_token再次发起之前失败的请求,从而实现了用户的token的更新操作。 #### 引用[.reference_title] - *1* [前后端处理实时刷新refresh_token的使用](https://blog.csdn.net/qq_41522141/article/details/123699113)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Refresh Token介绍](https://blog.csdn.net/NSPOKS/article/details/101771817)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [关于refresh token的总结](https://blog.csdn.net/MPFLY/article/details/123199084)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值