目前存在问题
我们之前可以使用退出登录接口直接退出,用户Session中的验证信息也会被销毁,但是现在是无状态的,用户来管理Token令牌,服务端只认Token是否合法,那这个时候该怎么让用户正确退出登录呢?
首先我们从最简单的方案开始,我们可以直接让客户端删除自己的JWT令牌,这样不就相当于退出登录了吗.
这样虽然是最简单粗暴的,但是存在一个问题**,用户可以自行保存这个Token拿来使用**。虽然客户端已经删除掉了,但是这个令牌仍然是可用的,如果用户私自保存过,那么依然可以正常使用这个令牌,这显然是有问题的。
目前有两种比较好的方案:
- 黑名单方案:所有黑名单中的JWT将不可使用。
- 白名单方案:不在白名单中的JWT将不可使用。
黑名单机制实现
这里我们以黑名单机制实现,这里使用redis实现黑名单机制。
导入redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
编写令牌失效方法
在JwtUtils
内,编写令牌失效方法
这里先校验令牌,获取令牌,在将他传入加入黑名单的方法
我们创建jwt令牌时,是设置了uuid的,这样确保uuid的唯一性;这里先验证token是不是存在黑名单中,不在就设置过期时间,并将其存入redis中,我们还要设置常量,来区分redis存的内容,因为之后还要存redis
到此就实现了黑名单
/** * 让指定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)); }
完善解析令牌方法
完善处理器
回到SecurityConfiguration
完善登出处理器
我们照常设置编码类型,再获取令牌,最后调用加入黑名单方法
ok~~,请自行测试