dolyw/ShiroJwt

重复请求会不会生成多个token

javabye opened this issue · 10 comments

同一个页面一般情况下都会同时请求多个后台接口,当accessToken失效的时候,同时有多个请求,后端会不会刷新出多个newtoken的问题?

防止重复刷新accessToken,有什么解决方案吗?

dolyw commented

正常情况下是不会的,多个请求下,某一个一旦刷新了AccessToken,其他请求如果不是携带最新的AccessToken是无法通过验证的

那一个页面请求多个接口,第一个接口把token刷新了。后面不就请求失败了吗?

dolyw commented

那一个页面请求多个接口,第一个接口把token刷新了。后面不就请求失败了吗?

一般同时请求多个接口应该是只有axios可以吧,应该可以异步回调解决吧,这是前端处理了,我前端不是很熟

OK 好的,暂时还没遇到这种问题。以后观察看看

dolyw commented

其实token失效,自动刷新token,在页面只有一个请求的时候是比较好处理的,但是如果页面同时有多个请求,并且前面的请求正好产生token失效,这就需要一些稍微复杂的处理

解决方式主要是用了Promise 函数来进行处理。前端每一个token失效的请求(后端返回状态码给前端,知道这个请求是token过期了)都会存到一个Promise函数集合里面,当刷新token的函数执行完毕后,才会批量再执行这些Promise函数,返回请求结果。还有一点要注意一下,这儿设置一个刷新token的开关isRefreshing,这个是非常有必要的,防止重复请求

详细链接:https://segmentfault.com/a/1190000016946316

@javabye 我也遇到这样的问题了,多个请求同时访问,正好refreshToken失效,前一个请求更新了refreshToken和accessToken,后一个请求就报出失效错误了,这是因为,前一个请求流程还没有走完回到前端,没来的及更新前端的accessToken,后一个请求带着之前旧的的accessToken请求就来了,而此时前一个请求已经更新了refreshToken,自然和后一个请求accessToken里的时间戳对不上了。下面说下我的解决方案,希望大家提提意见,方法是服务始终保留两个时间戳,简单的画了个图,
image
① 登录获取token1,存储到前端,下次请求都带token1,服务器存储timestamp1;
② 在token1正好失效时间点,多个请求同时访问服务器,请求A先被捕获到超时,要更新accessToken,取出服务器存放的时间戳,逗号分割后数组,如果此时只有一个时间戳,那就直接生成一个新的时间戳timestamp2,生成新accessToken,token2,此时把timestamp1和timestamp2,一起存储在服务端,请求A返回新的token2,此时,请求B过来后,发现token1过期,进入刷新方法,一样,拿出服务器中的时间戳,此时时间戳为timestamp1,timestamp2,我们发现和数组前一个时间戳一致,就直接请求放行,不再做refreshToken操作,正常业务结束后返回前端;
③ token2正好失效的时间点,请求C和D同时访问服务器端,请求C先被捕获超时需要更新,此时发现时间戳与服务的timestamp1,timestamp2数组后一个相等,那么生成新的timestamp3,生成新的accessToken token3,服务端refreshToken时间戳变成timestamp2,timestamp3,请求D进来后发现与数组前一个timestamp2相等,直接放行,不再做refreshToken操作,正常业务结束后返回前端。

dolyw commented

@javabye 我也遇到这样的问题了,多个请求同时访问,正好refreshToken失效,前一个请求更新了refreshToken和accessToken,后一个请求就报出失效错误了,这是因为,前一个请求流程还没有走完回到前端,没来的及更新前端的accessToken,后一个请求带着之前旧的的accessToken请求就来了,而此时前一个请求已经更新了refreshToken,自然和后一个请求accessToken里的时间戳对不上了。下面说下我的解决方案,希望大家提提意见,方法是服务始终保留两个时间戳,简单的画了个图,
image
① 登录获取token1,存储到前端,下次请求都带token1,服务器存储timestamp1;
② 在token1正好失效时间点,多个请求同时访问服务器,请求A先被捕获到超时,要更新accessToken,取出服务器存放的时间戳,逗号分割后数组,如果此时只有一个时间戳,那就直接生成一个新的时间戳timestamp2,生成新accessToken,token2,此时把timestamp1和timestamp2,一起存储在服务端,请求A返回新的token2,此时,请求B过来后,发现token1过期,进入刷新方法,一样,拿出服务器中的时间戳,此时时间戳为timestamp1,timestamp2,我们发现和数组前一个时间戳一致,就直接请求放行,不再做refreshToken操作,正常业务结束后返回前端;
③ token2正好失效的时间点,请求C和D同时访问服务器端,请求C先被捕获超时需要更新,此时发现时间戳与服务的timestamp1,timestamp2数组后一个相等,那么生成新的timestamp3,生成新的accessToken token3,服务端refreshToken时间戳变成timestamp2,timestamp3,请求D进来后发现与数组前一个timestamp2相等,直接放行,不再做refreshToken操作,正常业务结束后返回前端。

这有一点点复杂啊,哈哈,不过写一次就好了,好像看这个,前端也可以处理的:https://segmentfault.com/a/1190000016946316

dolyw commented

@javabye 我也遇到这样的问题了,多个请求同时访问,正好refreshToken失效,前一个请求更新了refreshToken和accessToken,后一个请求就报出失效错误了,这是因为,前一个请求流程还没有走完回到前端,没来的及更新前端的accessToken,后一个请求带着之前旧的的accessToken请求就来了,而此时前一个请求已经更新了refreshToken,自然和后一个请求accessToken里的时间戳对不上了。下面说下我的解决方案,希望大家提提意见,方法是服务始终保留两个时间戳,简单的画了个图,
image
① 登录获取token1,存储到前端,下次请求都带token1,服务器存储timestamp1;
② 在token1正好失效时间点,多个请求同时访问服务器,请求A先被捕获到超时,要更新accessToken,取出服务器存放的时间戳,逗号分割后数组,如果此时只有一个时间戳,那就直接生成一个新的时间戳timestamp2,生成新accessToken,token2,此时把timestamp1和timestamp2,一起存储在服务端,请求A返回新的token2,此时,请求B过来后,发现token1过期,进入刷新方法,一样,拿出服务器中的时间戳,此时时间戳为timestamp1,timestamp2,我们发现和数组前一个时间戳一致,就直接请求放行,不再做refreshToken操作,正常业务结束后返回前端;
③ token2正好失效的时间点,请求C和D同时访问服务器端,请求C先被捕获超时需要更新,此时发现时间戳与服务的timestamp1,timestamp2数组后一个相等,那么生成新的timestamp3,生成新的accessToken token3,服务端refreshToken时间戳变成timestamp2,timestamp3,请求D进来后发现与数组前一个timestamp2相等,直接放行,不再做refreshToken操作,正常业务结束后返回前端。

想了一下还是后端处理最安全,前端处理还是不行

dolyw commented

重新开了一个 Issues,说明了前端还有后端怎么解决 Token 刷新下,并发请求怎么处理的问题

就是同时多个请求,第一个请求刷新了 Token,后面的请求还是携带的刷新前的旧 Token 就无法通过,该怎么处理

#29 Token刷新并发处理