JSON Web Token (JWT) 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。由于其体积小、易于传输的特性,常被用于身份认证和授权。本文将介绍如何在纯 Java 环境以及 Spring Boot 项目中集成和使用 JWT。
一. 在 Java 中使用 JWT
在标准的 Java 环境中使用 JWT,通常分为三个步骤。
1. 导入依赖
首先,在您的 Maven 项目的 pom.xml 文件中引入 java-jwt 库。
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.1</version>
</dependency>2. 生成 Token
生成 Token 时,可以定义 payload(载荷)中的自定义信息、设置过期时间,并使用一个密钥(secret)进行签名。
// 1. 设置过期时间,例如:120秒后
Calendar time = Calendar.getInstance();
time.add(Calendar.SECOND, 120);
// 2. 创建 JWT 并签名
String token = JWT.create()
.withClaim("userId", 12) // 设置 payload 部分的自定义声明
.withClaim("username", "Heaven")
.withExpiresAt(time.getTime()) // 设置过期时间
.sign(Algorithm.HMAC256("!#^DHAD%&(")); // 使用 HMAC256 算法和密钥进行签名
System.out.println(token);生成的 Token 是一串由三部分(Header, Payload, Signature)组成的字符串,用 . 分隔。
3. 解析并验证 Token
接收到 Token 后,需要使用相同的密钥和算法来验证其合法性,并解析出其中的数据。如果验证失败(如签名不匹配、Token 过期),将会抛出异常。
// 1. 使用相同的算法和密钥创建验证器
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!#^DHAD%&(")).build();
// 2. 验证 token
DecodedJWT decodedJWT = jwtVerifier.verify(token);
// 3. 从解析后的 token 中获取数据
System.out.println(decodedJWT.getClaim("userId").asLong());
System.out.println(decodedJWT.getClaim("username").asString());
System.out.println(decodedJWT.getExpiresAt());二. 在 Spring Boot 中集成 JWT
在 Spring Boot 项目中,通常将 JWT 验证逻辑与拦截器(Interceptor)结合,以实现对 API 接口的统一认证和授权管理。
提示:常见的 JWT 验证异常
SignatureVerificationException: 签名不一致或无效。TokenExpiredException: Token 已过期。AlgorithmMismatchException: 使用的算法与 Token 中的算法不匹配。InvalidClaimException:payload中的声明无效。
1. 编写 JWT 工具类
首先,创建一个工具类来封装 Token 的生成和验证逻辑,便于在项目中复用。
package com.heaven.report.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
// 定义一个固定的密钥,实际项目中建议从配置文件读取
private static final String SECRET = "FFz%A#ZYj8C5AtHfkaDX8qPK7QUDeKce16rHWMKREMORpa8eue&2sdwFXa0oFVhN";
/**
* 生成 Token
* @param payloadMap 包含在 payload 中的数据
* @return 生成的 Token 字符串
*/
public static String createToken(Map<String, String> payloadMap) {
Calendar instance = Calendar.getInstance();
// 默认设置为3天后过期
instance.add(Calendar.DATE, 3);
JWTCreator.Builder builder = JWT.create();
// 将 payload 数据添加到 token 中
payloadMap.forEach(builder::withClaim);
String token = builder.withExpiresAt(instance.getTime()) // 设置过期时间
.sign(Algorithm.HMAC256(SECRET)); // 设置签名算法和密钥
return token;
}
/**
* 验证 Token 并返回解码后的信息
* @param token 待验证的 Token
* @return DecodedJWT 对象,包含了 token 中的信息
*/
public static DecodedJWT verify(String token) {
// 如果验证失败,此方法会抛出异常
return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
}
}2. 创建拦截器
创建一个 Spring MVC 拦截器,在请求到达 Controller 之前,从请求头中获取 Token 并进行验证。
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();
// 从请求头中获取 token
String token = request.getHeader("token");
try {
// 使用工具类验证 token
JWTUtils.verify(token);
// 验证通过,放行请求
return true;
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "无效的签名");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "Token 已过期");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "Token 算法不一致");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "Token 无效");
}
// 设置响应状态为 false
map.put("state", false);
// 将 map 转换为 JSON 字符串并返回给前端
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
// 拦截请求
return false;
}
}3. 注册并配置拦截器
最后,创建一个配置类实现 WebMvcConfigurer 接口,将拦截器注册到 Spring Boot 应用中,并指定需要拦截和排除的路径。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/api/user/login"); // 排除登录等不需要验证的接口
}
}通过以上配置,除了 /api/user/login 接口外,所有其他接口的请求都会被 JWTInterceptor 拦截和验证。