本文目录导读:

这是一个关于Java实现JWT(JSON Web Token)的完整案例教程,我将从依赖引入、核心工具类、生成与验证Token,到实际在Spring Boot项目中的应用,一步步为你说明。
添加Maven依赖
在你的 pom.xml 中添加JWT相关的依赖,最常用的是 jjwt 库。
<dependencies>
<!-- JWT核心库 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Web (如果你是用Spring Boot) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
注意:
jjwt-api是编译依赖,jjwt-impl和jjwt-jackson是运行时依赖(运行和测试时需要)。
创建JWT工具类
这是一个通用的JWT工具类,包含生成Token、解析Token、验证Token 的核心方法。
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public class JwtUtil {
// 1. 签名密钥(生产环境建议使用Base64编码的强密钥,不要硬编码)
private static final String SECRET = "YourSuperSecretKeyForJWT2024YoursMustBeAtLeast256BitsLong!!";
// 2. Token有效期(毫秒),这里设为1小时
private static final long EXPIRATION = 3600_000L;
// 3. 生成签名密钥对象
private static SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
}
// 4. 生成JWT Token
public static String generateToken(String userId, String username) {
Date now = new Date();
Date expirationDate = new Date(now.getTime() + EXPIRATION);
return Jwts.builder()
.subject(userId) // 主题:用户ID
.claim("username", username) // 自定义声明
.issuedAt(now) // 签发时间
.expiration(expirationDate) // 过期时间
.signWith(getSigningKey()) // 签名
.compact();
}
// 5. 解析Token,获取Claims(包含所有声明)
public static Claims parseToken(String token) {
return Jwts.parser()
.verifyWith(getSigningKey()) // 验证签名
.build()
.parseSignedClaims(token)
.getPayload();
}
// 6. 从Token中获取用户ID(即subject)
public static String getUserId(String token) {
return parseToken(token).getSubject();
}
// 7. 判断Token是否过期
public static boolean isTokenExpired(String token) {
try {
Claims claims = parseToken(token);
return claims.getExpiration().before(new Date());
} catch (ExpiredJwtException e) {
return true;
}
}
// 8. 验证Token是否有效(未过期且签名正确)
public static boolean validateToken(String token) {
try {
parseToken(token);
return !isTokenExpired(token);
} catch (JwtException | IllegalArgumentException e) {
// 签名无效、格式错误、过期等均视为无效
return false;
}
}
}
关键点说明
- 签名密钥:
SECRET至少需要256位(32字节),否则jjwt会报错。 - 声明(Claims):
subject通常存储用户唯一标识(如用户ID),自定义信息通过claim()添加。 - 验证:
verifyWith()方法会自动验证签名是否匹配。
在Spring Boot中集成JWT(示例)
1 用户登录接口(返回Token)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 1. 实际项目中这里应该验证用户名密码(此处简化)
if (!"admin".equals(request.getUsername())
|| !"123456".equals(request.getPassword())) {
return ResponseEntity.status(401).body("用户名或密码错误");
}
// 2. 验证通过,生成JWT Token
String userId = "1001"; // 假设从数据库获取
String token = JwtUtil.generateToken(userId, request.getUsername());
// 3. 返回Token给客户端
return ResponseEntity.ok(new LoginResponse(token));
}
}
// 请求体
class LoginRequest {
private String username;
private String password;
// 省略 getter/setter
}
// 响应体
class LoginResponse {
private String token;
public LoginResponse(String token) { this.token = token; }
// 省略 getter/setter
}
2 添加JWT拦截器(验证Token)
创建一个过滤器,拦截非登录请求并验证Token。
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// 1. 放行登录接口
if (request.getRequestURI().equals("/api/auth/login")) {
filterChain.doFilter(request, response);
return;
}
// 2. 从请求头获取Token(格式:Bearer <token>)
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
sendError(response, "缺少Token");
return;
}
String token = authHeader.substring(7); // 去掉"Bearer "
// 3. 验证Token
if (!JwtUtil.validateToken(token)) {
sendError(response, "Token无效或已过期");
return;
}
// 4. Token有效,将用户信息存入请求上下文(后续Controller可用)
String userId = JwtUtil.getUserId(token);
request.setAttribute("userId", userId);
// 5. 继续执行后续过滤器链
filterChain.doFilter(request, response);
}
private void sendError(HttpServletResponse response, String message)
throws IOException {
response.setStatus(401);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"error\":\"" + message + "\"}");
}
}
3 注册过滤器(Spring Boot配置)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtAuthenticationFilter jwtFilter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtFilter)
.addPathPatterns("/api/**") // 拦截所有/api下的请求
.excludePathPatterns("/api/auth/**"); // 排除登录
}
}
测试流程
1 获取Token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'
响应:
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDAxIiwidXNlcm5hbWUiOiJhZG1pbiIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ.xxxxxx"
}
2 携带Token访问受保护接口
curl http://localhost:8080/api/users/profile \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
常见问题与优化建议
| 问题 | 解决方案 |
|---|---|
| 密钥硬编码不安全 | 将密钥放在 application.yml 中,通过 @Value 注入 |
| Token无法刷新 | 实现刷新Token机制(参考OAuth2,返回access_token + refresh_token) |
| Token在黑名单中 | 使用Redis存储已注销的Token(如在用户登出时) |
| 不支持无状态 | JWT本身是无状态的,但黑名单方案会引入状态,根据需求取舍 |
优化后的密钥配置(application.yml)
jwt: secret: YourSuperSecretKeyForJWT2024YoursMustBeAtLeast256BitsLong!! expiration: 3600000 # 毫秒
然后在工具类中使用 @Value 注入:
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
// ... 其余代码改为非静态方法
}
通过以上案例,你实现了:
- ✅ 生成JWT:包含用户标识和自定义声明,设置过期时间。
- ✅ 验证JWT:检查签名完整性、是否过期。
- ✅ 集成Spring Boot:通过过滤器自动验证每个API请求的Token。
- ✅ 安全实践:强密钥、统一错误返回、可配置化密钥。
这个结构可以直接应用于你的项目,只需根据业务调整用户信息的内容和验证逻辑即可。