JWT
JSON Web Token(JSON Web令牌)
是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
JWT作用:
授权:一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。它的开销很小并且可以在不同的域中使用。如:单点登录。
信息交换:在各方之间安全地传输信息。JWT可进行签名(如使用公钥/私钥对),因此可确保发件人。由于签名是使用标头和有效负载计算的,因此还可验证内容是否被篡改。
参考链接:https://blog.csdn.net/Top_L398/article/details/109361680
本项目中,使用JWT实现用户登录,以及后续用户身份验证的功能。
应用—用户登录和验证
1.在API(Controller)层创建接口
1 2 3 4 5
| @PostMapping("/user-tokens") public JsonResponse<String> login(@RequestBody User user) throws Exception{ String token = userService.login(user); return new JsonResponse<>(token); }
|
2.在Service层对user信息进行处理。其中密码部分需要前端对明文密码进行加密,后端拿到之后再进行解密,最后逐一判断,生成并返回token。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public String login(User user) throws Exception{ String phone = user.getPhone() == null ? "" : user.getPhone(); String email = user.getEmail() == null ? "" : user.getEmail(); if(StringUtils.isNullOrEmpty(phone) && StringUtils.isNullOrEmpty(email)){ throw new ConditionException("参数异常!"); } User dbUser = userDao.getUserByPhoneOrEmail(phone, email); if(dbUser == null){ throw new ConditionException("当前用户不存在!"); } String password = user.getPassword(); String rawPassword; try{ rawPassword = RSAUtil.decrypt(password); }catch (Exception e){ throw new ConditionException("密码解密失败!"); } String salt = dbUser.getSalt(); String md5Password = MD5Util.sign(rawPassword, salt, "UTF-8"); if(!md5Password.equals(dbUser.getPassword())){ throw new ConditionException("密码错误!"); } return TokenUtil.generateToken(dbUser.getId()); }
|
3.TokenUtil类,用于生成验证用户身份的Token,需要使用JWT生成Token。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class TokenUtil {
private static final String ISSUER = "签发者";
public static String generateToken(Long userId) throws Exception{ Algorithm algorithm = Algorithm.RSA256(RSAUtil.getPublicKey(), RSAUtil.getPrivateKey()); Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.HOUR, 1); return JWT.create().withKeyId(String.valueOf(userId)) .withIssuer(ISSUER) .withExpiresAt(calendar.getTime()) .sign(algorithm); }
public static String generateRefreshToken(Long userId) throws Exception{ Algorithm algorithm = Algorithm.RSA256(RSAUtil.getPublicKey(), RSAUtil.getPrivateKey()); Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.DAY_OF_MONTH, 7); return JWT.create().withKeyId(String.valueOf(userId)) .withIssuer(ISSUER) .withExpiresAt(calendar.getTime()) .sign(algorithm); }
public static Long verifyToken(String token){ try{ Algorithm algorithm = Algorithm.RSA256(RSAUtil.getPublicKey(), RSAUtil.getPrivateKey()); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); String userId = jwt.getKeyId(); return Long.valueOf(userId); }catch (TokenExpiredException e){ throw new ConditionException("555","token过期!"); }catch (Exception e){ throw new ConditionException("非法用户token!"); } } }
|
4.身份认证,每次需要验证用户身份时,向前端获取请求头,分离出其中的信息并验证,从而获取用户身份。第二个方法用于双令牌登录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Component public class UserSupport {
@Autowired private UserService userService;
public Long getCurrentUserId() { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); String token = request.getHeader("token"); Long userId = TokenUtil.verifyToken(token); if(userId < 0) { throw new ConditionException("非法用户"); }
return userId; }
private void verifyRefreshToken(Long userId){ ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); String refreshToken = requestAttributes.getRequest().getHeader("refreshToken"); String dbRefreshToken = userService.getRefreshTokenByUserId(userId); if(!dbRefreshToken.equals(refreshToken)){ throw new ConditionException("非法用户!"); } }
}
|