虽然我们使用JWT已经很方便了,但是有一个很严重的问题就是,我们没办法像Session那样去踢用户下线,什么意思呢?我们之前可以使用退出登录接口直接退出,用户Session中的验证信息也会被销毁,但是现在是无状态的,用户来管理Token令牌,服务端只认Token是否合法,那这个时候该怎么让用户正确退出登录呢?
首先我们从最简单的方案开始,我们可以直接让客户端删除自己的JWT令牌,这样不就相当于退出登录了吗,这样甚至不需要请求服务器,直接就退了
这样虽然是最简单粗暴的,但是存在一个问题,用户可以自行保存这个Token拿来使用。虽然客户端已经删除掉了,但是这个令牌仍然是可用的,如果用户私自保存过,那么依然可以正常使用这个令牌,这显然是有问题的。
目前有两种比较好的方案:
-
黑名单方案:所有黑名单中的JWT将不可使用。
-
白名单方案:不在白名单中的JWT将不可使用。
这里我们以黑名单机制为例,让用户退出登录之后,无法再次使用之前的JWT进行操作,首先我们需要给JWT额外添加一个用于判断的唯一标识符,这里就用UUID好了
我们使用redis实现黑名单
依赖导入:
```Plain Text org.springframework.boot spring-boot-starter-data-redis
在jwt工具类中添加让令牌失效的方法:
Java public boolean invalidateJwt(String headerToken){ String token=this.convertToken(headerToken); if (token==null){ return false; } Algorithm algorithm=Algorithm.HMAC256(key); JWTVerifier jwtVerifier = JWT.require(algorithm).build(); try {
}
}
可以将令牌直接存到redis里面,更简单的方法是给令牌加个id,查找时直接通过id比较,因此在创建令牌是增加jwtid:
Java 在create方法中添加 .withJWTId(UUID.randomUUID().toString())
在try中获取令牌对应的id并进行检验,确定是否存在和是否过期:
Java try { DecodedJWT jwt=jwtVerifier.verify(token); String id=jwt.getId(); return deleteToken(id,jwt.getExpiresAt()); }catch (JWTVerificationException e){ return false; }
对应删除和检验方法的实现:
Java private boolean deleteToken(String uuid,Date time){ if (isInvalidToken(uuid)){ return false; } Date now=new Date();; long expire=Math.max(time.getTime()-now.getTime(),0); template.opsForValue().set(Const.JWTBLACKLIST+uuid,"",expire, TimeUnit.MILLISECONDS); return true; }
private boolean isInvalidToken(String uuid ){
return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST+uuid));
}
创建const类保存jwt黑名单
Java public class Const { public static final String JWTBLACKLIST="jwt:blacklist:"; }
编写logout方法:
Java public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
String authorization=request.getHeader("Authorization");
if (utils.invalidateJwt(authorization)){
writer.write(RestBean.success().asJsonString());
}
else {
writer.write(RestBean.failure(400,"token失效").asJsonString());
}
System.out.println("logoutsuccess");
}
}
```
在JWT中,所有颁发的token都可以再次使用,通过在服务器设立黑名单机制,才可以真正的实现用户的退出登陆。