使用黑名单完成,Jwt退出登录操作

1,200 阅读2分钟

目前存在问题

我们之前可以使用退出登录接口直接退出,用户Session中的验证信息也会被销毁,但是现在是无状态的,用户来管理Token令牌,服务端只认Token是否合法,那这个时候该怎么让用户正确退出登录呢?

首先我们从最简单的方案开始,我们可以直接让客户端删除自己的JWT令牌,这样不就相当于退出登录了吗.

这样虽然是最简单粗暴的,但是存在一个问题**,用户可以自行保存这个Token拿来使用**。虽然客户端已经删除掉了,但是这个令牌仍然是可用的,如果用户私自保存过,那么依然可以正常使用这个令牌,这显然是有问题的。

目前有两种比较好的方案:

  • 黑名单方案:所有黑名单中的JWT将不可使用。
  • 白名单方案:不在白名单中的JWT将不可使用。

黑名单机制实现

这里我们以黑名单机制实现,这里使用redis实现黑名单机制。

导入redis依赖

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

编写令牌失效方法

JwtUtils内,编写令牌失效方法

这里先校验令牌,获取令牌,在将他传入加入黑名单的方法

image-20230810161432777

我们创建jwt令牌时,是设置了uuid的,这样确保uuid的唯一性;这里先验证token是不是存在黑名单中,不在就设置过期时间,并将其存入redis中,我们还要设置常量,来区分redis存的内容,因为之后还要存redis

image-20230810161831231

image-20230810162708567

image-20230810162101210

到此就实现了黑名单

  /**
      * 让指定Jwt令牌失效
      * @param headerToken 请求头中携带的令牌
      * @return 是否操作成功
      */
     public  boolean invalidate(String headerToken){
         // 校验headerToken
         String token = this.convertToken(headerToken);
         if (token == null)
             return false;
         Algorithm algorithm = Algorithm.HMAC256(key);
         JWTVerifier jwtVerifier = JWT.require(algorithm).build();
         try {
             DecodedJWT verify = jwtVerifier.verify(token);
             // 加入黑名单
             return deleteToken(verify.getId(),verify.getExpiresAt());
         } catch (JWTVerificationException e) {
             return false;
         }
     }
     /**
      * 将Token列入Redis黑名单中
      * @param uuid 令牌ID
      * @param time 过期时间
      * @return 是否操作成功
      */
     public boolean deleteToken(String uuid, Date time) {
         // 验证Token是否被列入Redis黑名单
         if (isInvalidToken(uuid)) {
             return false;
         }
         // 验证是否过期
         Date now = new Date();
         long expire =Math.max(time.getTime()-now.getTime(), 0);
         // 将Token列入Redis黑名单中
         redisTemplate.opsForValue().set(Const.JWT_BLACK_LIST + uuid,"", expire,  TimeUnit.MILLISECONDS);
         return true;
     }
 ​
     /**
      * 验证Token是否被列入Redis黑名单
      * @param uuid 令牌ID
      * @return 是否操作成功
      */
     private boolean isInvalidToken(String uuid){
         return Boolean.TRUE.equals(redisTemplate.hasKey(Const.JWT_BLACK_LIST + uuid));
     }

完善解析令牌方法

image-20230810162543251

完善处理器

回到SecurityConfiguration完善登出处理器

我们照常设置编码类型,再获取令牌,最后调用加入黑名单方法

image-20230810162932046

ok~~,请自行测试