结合请求拦截和响应拦截实现双token机制
笔者从一开始写博客就说明了本项目将使用双token机制,大大满足用户体验以及用户账号信息的安全性能。 但是笔者在之前的测试中,与负责后端的同学交流过,先用单token机制进行测试 —— 因为双token机制实现起来就是对请求拦截和响应拦截进行丰富。 笔者在之前也曾说过,拦截什么的实现逻辑完全可以放在项目接近尾声的时候进行实现。故话不多说,且看下文。
实现双token机制流程
- 修改原登录设置单token的逻辑 —— 在登录成功后,将双token存储下来。
- 回顾之前的请求拦截器和响应拦截器。 从下图的博客可以看到:
(1)笔者之前的请求拦截器就是直接在发送的所有请求的请求头上加上token字段。显然这是不对的,因为登录请求的请求头是不需要该字段的。 此外因为要实现双token机制,由于向服务器发起获取新的双token是需要将refreshToken作为请求头发送出去的。 (2)笔者之前的响应拦截器几乎没做什么拦截,只要状态码是200的则放行,而其他状态码则给用户提示出了什么错误。 而为了实现双token机制,则需要拦截accessToken过期的状态码,然后用refreshToken当作请求头向服务器发起获取新的双token的请求,这里有两种情况:1. 成功获取新的双token,则需要向服务器重新发起用户之前发起的所有请求;2. refreshToken也过期了导致无法获取新的双token,则给用户提示账号权限失效,需要重新登录,然后跳转到登录页面。 - 根据上述的所说,先解决请求拦截器存在的问题。当然这问题也很容易解决。且看下图。
- 实现响应拦截器。
- 第一个要解决的问题:如何发起获取新的双token请求?
- 第二个要解决的问题:如何存储用户原发起的请求?因为如果获取新的双token成功,则需要用到用户原新的请求。
- 第三个要解决的问题:获取失败后的逻辑具体实现。
- 对于第一个问题,笔者在前文有提到,发起获取新token请求前,会对该请求进行拦截,修改请求头配置。然后就可以发起请求了。
- 对于第二个问题,也挺好实现的,直接将原config参数传递给响应拦截器,即存储下来了。
- 对于第三个问题,因为双token都过期了,那首先需要移除用户中原来的双token,然后再给用户提示,最后跳转到登录页面。
结合请求拦截和响应拦截实现双token机制代码
请求拦截 响应拦截 其余状态码响应拦截根据项目需求进行拦截。基本不用做什么特殊处理,直接提示用户即可。
可优化部分——解决并发请求问题
首先需要了解什么是并发请求。
并发请求
虽说是并发请求,但从微观上看,程序代码是有先后性的,网络请求也是存在先后顺序的,所以这并不是严格意义上的并发请求;而从宏观上看,由于程序发起这些请求的时间过于短暂,使得网络请求让人看起来像是并发请求一样,故而称为并发请求。
存在的问题
结合上述代码实现逻辑,那就存在一个问题:倘若存在并发请求,而这些并发请求携带的 accessToken 恰好又过期了,那便会向服务器发起多次获取新的双 token 请求,可能导致性能方面有所损耗。
解决思路
当然笔者对这可优化部分也有相应的实现思路:1、设置源请求缓冲区和锁;2、加锁;3、重新发起源队列中的请求;4、释放锁
1、设置源请求缓冲区和锁:设置一个全局队列,由于并发请求实际上是有顺序的,即按用户源请求顺序将请求存入队列中。设置全局锁,用于控制是否可以发起获取新的双 token 请求。
2、加锁:由于并发请求携带的 accessToken 是过期的,则会有多个响应在返回的时候会被响应拦截器拦截下来。当第一个响应被拦截下来的时候,则对获取新的双 token 请求进行加锁——防止后续响应被拦截进来的时候继续执行该请求。
3、重新发起源请求缓冲区中的请求:发起条件则是成功获取新的双 token 后 (可使用 async 和 await 实现) ,按队列顺序依次发起用户原请求。
4、释放锁:释放锁的条件就是成功发起所有源请求后 (可使用 async 和 await 实现) ,释放锁。当然释放锁的同时,将源请求缓冲区置为空。
探索精神(自夸一波。。)
至于笔者为什么没有去实现这样的优化呢?
答: (1)由于笔者代码的实现逻辑上,会产生并发请求的可能性较低; (2)实现上述优化则需要申请全局队列以及全局锁,同时无论发起的是什么请求,都需要将请求存入队列中,然后大部分情况下都是需要将该队列内容进行清空的,实际上并没有这个必要,诚然这也算是另一方面的性能损耗;
综合上述考虑,目前实现逻辑的情况是:发起多次获取新token的请求可能性较低,并且在性能方面的损耗是可以接收的,笔者便觉得不是很有必要再深入优化了。但这并不能阻止笔者去探索这其中存在的问题不是吗?—— 倘若以后遇到类似的问题,那也算是有经验了啊。
|