前后端分离,使用token验证用户信息,我选择了jwt,网上可以找到很多教程,就不介绍了。这里描述一下使用过程中最重要的环节刷新token导致的问题。业务要达到的目标:用户登录一次后,前端保存token,每次向后端请求时,header中携带授权信息,后端从请求中解析token,根据token验证用户信息,并返回相应的信息。相信大部分看过文档并开始使用的同学到这里都已经走过了。以下是坑的开始:1.产品要求半个月内不登录,这里会用到refreshToken。jwt的设计思路很到位。:给前端发送的token设置一个有效期,比如2小时,前端发送的token在2小时后失效。这个时候我们可以根据发送的token来判断。如果token在2小时之外,并且正在刷新token的有效期(比如半个月以内),那么我们在返回数据给前端的时候返回一个新的token,前端收到这个token并且存储它,当它再次请求时,它发送一个新的令牌,等等。只要你在半个月内不间断地进入系统,那么你就根本不需要登录。2.问题1)如何将新的token发送到前端?这个问题的答案很简单,在响应头中设置授权。划重点:后台一般使用的域名都是二级域名。比如我的是api.xx.com,会和前端产生跨域影响。请记得设置`$response->headers->set('Access-Control-Expose-Headers','Authorization');`在设置跨域的时候,还需要设置一个Cache-Control。这个东西的问题真是莫名其妙,困扰我好久了。。`$response->headers->set('Cache-Control','no-store');//如果没有,会导致前端从缓存中获取headertoken`2)一般在中间件中刷新token,继续当前请求。controller中需要如何根据token获取登录用户信息?可能不能一下子说明问题,简单理解就是:token已经刷新了,所以当前token一定是无效的。如果在controller中继续在request中使用token,肯定会报token无效的错误。这里需要带上新的token给下面的程序处理,我这里改了当前的请求头,将请求头中的Authorization替换为newToken。3)并发请求。也就是说,2小时后,同一个页面发送了2个请求。这是正常的,比如请求列表数据,请求搜索表单。既然token已经过期,那么返回2个新的token?我发现了这个问题并在github中看到了这个问题,但没有人回答。jwt肯定不会发回两个新的token,所以一定有一个token不仅无效,而且刷新当前token后,又会生成一个新的token。令牌已添加到备份列表且无法使用,因此另一个请求无法成功。我这里是用Redis解决的,以旧token为key,新token为value,设置过期时间为30秒。当第二次请求来的时候,我们已经知道这个token在backlist中了,我们可以去redis中查看是否有这样一个旧的token,存在则放行。3.关键中间件代码setRequest($request)->getToken()){returnresponse()->json(['code'=>'2','msg'=>'noparametertoken','数据'=>'',]);}尝试{$user=$auth->authenticate($token);if(!$user){returnresponse()->json(['code'=>'2','msg'=>'未找到用户信息','数据'=>'',]);}$request->headers->set('Authorization','Bearer'.$token);}catch(TokenExpiredException$e){try{sleep(rand(1,5)/100);$newToken=JWTAuth::refresh($token);$request->headers->set('Authorization','Bearer'.$newToken);//为当前请求设置一个token,以在本次请求中,需要调用用户信息。//将旧的token存入redis,30秒内再次请求有效。Redis::setex('token_blacklist:'.$token,30,$newToken);}catch(JWTException$e){//在黑名单有效期内,释放if($newToken=Redis::get('token_blacklist:'.$token)){$request->headers->set('Authorization','承载者'.$newToken);//为当前请求设置一个token,以防本次请求需要调用用户信息return$next($request);}//过期用户返回response()->json(['code'=>'2','msg'=>'帐号信息已过期,请重新登录',]);}}catch(JWTException$e){returnresponse()->json(['code'=>'2','msg'=>'invalidtoken','data'=>'',]);}$response=$next($request);if($newToken){$response->headers->set('Authorization','Bearer'.$newToken);}返回$响应;}}一整天就到这里了,通过实践就会发现问题,累并快乐着解决^_^===========================================================================================================================================================================三个问题,当时写的时候觉得恶心。作者发布了新版本(1.0.0-rc.1),config中多了一个配置'blacklist_grace_period'=>env('JWT_BLACKLIST_GRACE_PERIOD',60)当多个并发请求使用同一个JWT时,其中一些可能由于access_token的刷新而失败。以秒为单位设置请求时间,防止并发请求失败。您可以享受这个新版本,它可以很好地解决这个问题。中间件更新了部分:try{//刷新令牌,超时刷新会捕获$refresh=JWTAuth::parseToken()->refresh();$user=$auth->authenticate($refresh);//生成Newtoken(上面的$refresh也是一个合法的refreshtoken,这里如果直接使用,刷新2次就获取不到,亲测)$newToken=JWTAuth::fromUser($user);//为当前请求token设置属性,以防本次请求需要调用用户信息$request->headers->set('Authorization','Bearer'.$newToken);}
