资讯专栏INFORMATION COLUMN

解决使用jwt刷新token带来的问题

whataa / 3470人阅读

摘要:问题如何将新的发给前端比较好这个问题答案简单,在的中设置。我这里使用解决的,将旧作为键,新作为值,设置一个秒过期的时间。大家可以享用这个新版,可以很好解决这个问题。

前后端分离,使用token的方式校验用户信息,我选择了jwt,使用的教程在网上可以找到很多,不做介绍。

这里说明一个使用过程中,最重要的的一个环节刷新token带来的问题。

业务要达到的目标:

用户登录一次之后,前端保存token,后面每次向后端请求的时候,header都带上authorization信息,后端从请求中解析token,根据token验证用户信息,返回相应的信息。

相信大部分看过文档并开始使用的同学都已经走通到这里了,下面是入坑的开始:

1. 产品要求

半个月内免登陆,这里就要使用到了refreshToken了,jwt设计思想很到位:设置发给前端的token一个有效期,比如2个小时,2个小时候前端发来的token就会失效,这个时候我们根据发来的token判断下,如果这个token在2个小时外,并在刷新token的有效期内(比如半个月内),那么我们在给前端返回数据的时候返回一个新token,前端接到这个token存储起来,当再次请求的时候,发送新的token,如此周而复始,只要你在半个月内没有间断去进入系统,那么完全不需要去进行登录的操作。

2. 问题
1)如何将新的token发给前端比较好?

   这个问题答案简单,在response 的header中设置authorization。
   关键点:后端一般使用的域名是二级域名比如我的是api.xx.com,会和前端产生一个跨域的影响,请记得一定要设置
   `$response->headers->set("Access-Control-Expose-Headers", "Authorization");`
   设置跨域的时候还要设置一个Cache-Control,这个东西出现的问题真的是莫名其妙,坑了我很久..
   `$response->headers->set("Cache-Control", "no-store"); // 无的话会导致前端从缓存获取头token`

2) 一般是在中间件中刷新token,当前请求继续走,如何在controller中需要根据token调取登录用户信息?

   一下子可能没说明问题,简单理解为:token已经刷新了,那么当前token肯定失效了,继续在controller利用请求中的token肯定会报token失效的错误,这里需要将新token带到后面的程序处理中,我这里更改了当前请求头,将newToken替换了request header中的Authorization。

3) 并发请求。也就是2个小时候之后,同一个页面发来了2个请求,这个很正常,比如一个请求列表数据,一个请求搜索的表单,因为token都已失效,那么难道返回2个新的token回去?

   这个问题找了在github里面看到了issue但是无人回答,jwt肯定不会发两个新的token回去的,那麽肯定会有一个token不仅是失效了,刷新当前token之后,产生新的token,旧token加入到了backlist中了,无法使用,那么另外一个请求自然无法成功。我这里使用Redis解决的,将旧token作为键,新token作为值,设置一个30秒过期的时间。当第二个请求来的时候,已经知道token在backlist中了,我们可以去redis查询下是否存在这么个旧token,存在的话放行。

3. 关键中间件代码

   setRequest($request)->getToken()) {
               return response()->json([
                   "code" => "2",
                   "msg" => "无参数token",
                   "data" => "",
               ]);
           }
   
           try {
               $user = $auth->authenticate($token);
               if (! $user) {
                   return response()->json([
                       "code" => "2",
                       "msg" => "未查询到该用户信息",
                       "data" => "",
                    ]);
               }
               $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","Bearer ".$newToken); // 给当前的请求设置性的token,以备在本次请求中需要调用用户信息
                       return $next($request);
                   }
                   // 过期用户
                   return response()->json([
                       "code" => "2",
                       "msg" => "账号信息过期了,请重新登录",
                   ]);
               }
           } catch (JWTException $e) {
               return response()->json([
                   "code" => "2",
                   "msg" => "无效token",
                   "data" => "",
                ]);
           }
           $response = $next($request);
   
           if ($newToken) {
               $response->headers->set("Authorization", "Bearer ".$newToken);
           }
   
           return $response;
       }
   }
   

一整天的时间耗在这里了,实践才会发现问题,累并快乐着解决了^_^

=====================割了一了白了(2019)===================

关于第三个问题,当时写的时候就感觉很恶心,作者已经出了一个新版本(1.0.0-rc.1),config里面多了一项配置
"blacklist_grace_period" => env("JWT_BLACKLIST_GRACE_PERIOD", 60)
当多个并发请求使用相同的JWT进行时,由于 access_token 的刷新 ,其中一些可能会失败,以秒为单位设置请求时间以防止并发的请求失败。
大家可以享用这个新版,可以很好解决这个问题。中间件更新了部分:

try {
    // 刷新token,超时刷新将会去catch
    $refresh = JWTAuth::parseToken()->refresh();
    $user = $auth->authenticate($refresh);
    // 生成新token(上面的$refresh也是合法的刷新token,这里如果直接用,2次刷新之后再无法继续获得,亲测)
    $newToken = JWTAuth::fromUser($user);
    // 给当前的请求设置性的token,以备在本次请求中需要调用用户信息
    $request->headers->set("Authorization","Bearer ".$newToken);
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28232.html

相关文章

  • 如何在SpringBoot中集成JWT(JSON Web Token)鉴权

    摘要:在使用非对称加密算法进行签名的时候,还可以用于验证的发件人是否与中申明的发件人是同一个人。如果没有用非对称加密算法的话,把复制之后直接可以去官网在线解析。 这篇博客主要是简单介绍了一下什么是JWT,以及如何在Spring Boot项目中使用JWT(JSON Web Token)。 1.关于JWT 1.1 什么是JWT 老生常谈的开头,我们要用这样一种工具,首先得知道以下几个问题。 这...

    yeyan1996 评论0 收藏0
  • JWT过期,修改密码,注销登录如何处理

    摘要:设置过期时间每次登录,包括登录都返回一个可用的给客户端,保证合理登录的用户都可以用,实现不会过期的效果。直接将中该用户的信息过期。下次通过登录,会提醒失效,要重新登录,我们重新生成一个新的给用户,然后。。通过存储,实现过期失效的问题了。 JWT 使用场景:(自己总结的,每次请求携带token,然后到服务端验证token是否正确,是否过期,然后解码出携带的用户信息。服务端不需要再存储se...

    wdzgege 评论0 收藏0
  • 签发用户认证token超时刷新策略

    摘要:签发的用户认证超时刷新策略这个模块分离至项目权限管理系统与前后端分离实践,感觉那样太长了找不到重点,分离出来要好点。这样在有效期过后的时间段内可以申请刷新。 签发的用户认证token超时刷新策略 这个模块分离至项目api权限管理系统与前后端分离实践,感觉那样太长了找不到重点,分离出来要好点。 对于登录的用户签发其对应的jwt,我们在jwt设置他的固定有效期时间,在有效期内用户携带jw...

    e10101 评论0 收藏0

发表评论

0条评论

whataa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<