大家好,我是小趴菜,在工作中我们经常要做的一个就是登陆功能,然后获取这个用户的token,后续请求都会带上这个token来验证用户的请求。
//堆代码 duidaima.com //生成Token public static String generateToken(Map<String, Object> payloads) { Map<String, Object> map = new HashMap<>(2); map.put("alg", "HS256"); map.put("typ", "JWT"); Date date = new Date(System.currentTimeMillis() + EXPIRE); JWTCreator.Builder jwtBuilder = JWT .create() .withHeader(map) .withExpiresAt(date); for (Map.Entry<String, Object> entry : payloads.entrySet()) { jwtBuilder.withClaim(entry.getKey(), entry.getValue().toString()); } return jwtBuilder.sign(Algorithm.HMAC256(SECRET)); } //校验Token public static Map<String, Claim> verifyToken(String token) { try{ JWTVerifier verifier = JWT .require(Algorithm.HMAC256(SECRET)) .build(); DecodedJWT jwt = verifier.verify(token); return jwt.getClaims(); }catch (Exception e){ throw new GlobalException(ResponseEnums.TOKEN_VERIFY_FAIL_ERROR); } }我们会给每一个Token设置一个过期时间,前端拿到这个token以后,在之后的用户每一次请求都会带上这个Token进行校验,如果过期了或者Token格式不对,我们就不让请求通过,直接返回错误信息给前端
//从请求头中拿到token key : token String headerToken = request.getHeader(TokenConstant.TOKEN_HEADER); if (StrUtil.isBlank(headerToken)) { throw new GlobalException(ResponseEnums.TOKEN_IS_NULL_ERROR); } //解析token Map<String, Claim> claimMap = JwtUtil.verifyToken(headerToken); return true; }
这看上去是一件很美好的事情,因为我们解决了用户请求校验的问题,但是这个Token是存储在前端的缓存中的。当我们点击退出登陆的时候,前端也只是把缓存的这个Token给清掉,这样用户后续的请求就没有这个Token,也就会让用户去重新登陆了。这看起来是没有问题的。
但是如果你这个Token还没有过期,这时候你的Token被其他人截取了,这时候,即使你退出登陆但是这个Token一样是可以通过校验的。所以其他人还是可以拿你这个Token去请求对应的接口。这时候就会有安全问题了。那有没有解决办法呢?
其实是有的。我们可以把这个Token保存到Redis中。每次请求的时候,判断一下Redis中有没有这个Token,有就放行,没有就返回错误信息给前端。当用户点击退出登陆的时候,把Redis的这个Token给删除掉就行了,这样后续即使用人截取了你这个Token,由于Redis没有,那么第一步返回fasle,就可以直接返回错误信息给前端,不会去执行这个请求