Java JWT 实现完整教程
2025/9/17大约 6 分钟
Java JWT 实现完整教程
前置知识
在开始本教程之前,建议您具备以下基础知识:
- Java 基础语法
- Maven 或 Gradle 构建工具
- HTTP 协议基础
- 基本的加密概念
什么是 JWT?
JWT (JSON Web Token) 是一种开放标准 (RFC 7519),用于在各方之间安全地传输信息作为 JSON 对象。JWT 通常用于:
- 身份验证:用户登录后,后续请求都会包含 JWT
- 信息交换:JWT 可以安全地在各方之间传输信息
- 无状态会话:服务器不需要存储会话信息
JWT 结构
JWT 由三部分组成,用点 (.) 分隔:
Header.Payload.Signature
详细结构说明
- Header:包含令牌类型和签名算法
- Payload:包含声明(claims),即要传输的数据
- Signature:用于验证令牌的完整性和真实性
环境准备
1. 添加依赖
在您的 pom.xml
中添加 JWT 相关依赖:
<dependencies>
<!-- JWT 核心库 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JWT 实现 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JWT JSON 序列化 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2. 创建项目结构
src/main/java/com/example/jwt/
├── JwtUtil.java
├── User.java
├── JwtService.java
└── JwtController.java
核心实现
1. 用户实体类
package com.example.jwt;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Long id;
private String username;
private String email;
private String role;
}
2. JWT 工具类
点击查看JWT工具类代码
package com.example.jwt;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
// 密钥 - 生产环境中应该从配置文件读取
private static final String SECRET_KEY = "your-secret-key-must-be-at-least-256-bits-long";
// 令牌过期时间(毫秒)
private static final long EXPIRATION_TIME = 86400000; // 24小时
// 生成密钥
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
}
/**
* 生成 JWT 令牌
*/
public String generateToken(User user) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("username", user.getUsername());
claims.put("email", user.getEmail());
claims.put("role", user.getRole());
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
/**
* 验证 JWT 令牌
*/
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
/**
* 从令牌中获取用户名
*/
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
/**
* 从令牌中获取过期时间
*/
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
/**
* 从令牌中获取指定声明
*/
public <T> T getClaimFromToken(String token, java.util.function.Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
/**
* 从令牌中获取所有声明
*/
private Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
/**
* 检查令牌是否过期
*/
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}
3. JWT 服务类
点击查看JWT服务类代码
package com.example.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class JwtService {
@Autowired
private JwtUtil jwtUtil;
/**
* 用户登录并生成 JWT
*/
public String login(String username, String password) {
// 这里应该验证用户名和密码
// 为了演示,我们创建一个模拟用户
User user = authenticateUser(username, password);
if (user != null) {
return jwtUtil.generateToken(user);
}
throw new RuntimeException("用户名或密码错误");
}
/**
* 验证令牌并返回用户信息
*/
public User validateTokenAndGetUser(String token) {
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.getUsernameFromToken(token);
// 这里应该从数据库获取用户信息
return getUserByUsername(username);
}
throw new RuntimeException("无效的令牌");
}
/**
* 模拟用户认证
*/
private User authenticateUser(String username, String password) {
// 实际项目中应该查询数据库
if ("admin".equals(username) && "password".equals(password)) {
return new User(1L, "admin", "admin@example.com", "ADMIN");
}
return null;
}
/**
* 模拟根据用户名获取用户
*/
private User getUserByUsername(String username) {
if ("admin".equals(username)) {
return new User(1L, "admin", "admin@example.com", "ADMIN");
}
return null;
}
}
使用示例
1. Spring Boot 控制器
点击查看控制器代码
package com.example.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class JwtController {
@Autowired
private JwtService jwtService;
/**
* 用户登录
*/
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
try {
String token = jwtService.login(loginRequest.getUsername(), loginRequest.getPassword());
Map<String, Object> response = new HashMap<>();
response.put("token", token);
response.put("message", "登录成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}
/**
* 获取用户信息
*/
@GetMapping("/profile")
public ResponseEntity<?> getProfile(@RequestHeader("Authorization") String authHeader) {
try {
String token = authHeader.replace("Bearer ", "");
User user = jwtService.validateTokenAndGetUser(token);
return ResponseEntity.ok(user);
} catch (Exception e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}
}
// 登录请求类
class LoginRequest {
private String username;
private String password;
// Getter 和 Setter
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
2. 测试示例
package com.example.jwt;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JwtTest {
@Test
public void testJwtGeneration() {
JwtUtil jwtUtil = new JwtUtil();
User user = new User(1L, "testuser", "test@example.com", "USER");
String token = jwtUtil.generateToken(user);
System.out.println("生成的 JWT: " + token);
// 验证令牌
boolean isValid = jwtUtil.validateToken(token);
System.out.println("令牌是否有效: " + isValid);
// 获取用户名
String username = jwtUtil.getUsernameFromToken(token);
System.out.println("用户名: " + username);
}
}
最佳实践
安全注意事项
- 密钥管理:生产环境中不要硬编码密钥,使用环境变量或配置服务
- 密钥长度:确保密钥至少 256 位长
- HTTPS:始终使用 HTTPS 传输 JWT
- 过期时间:设置合理的令牌过期时间
性能优化
- 令牌大小:不要在 JWT 中存储过多数据
- 缓存策略:考虑使用 Redis 缓存用户信息
- 刷新令牌:实现刷新令牌机制
1. 环境变量配置
# application.properties
jwt.secret=your-super-secret-key-here-make-it-long-and-random
jwt.expiration=86400000
2. 刷新令牌实现
public class RefreshTokenService {
public String generateRefreshToken(User user) {
// 生成长期有效的刷新令牌
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 24 * 60 * 60 * 1000)) // 30天
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String refreshAccessToken(String refreshToken) {
// 验证刷新令牌并生成新的访问令牌
if (validateRefreshToken(refreshToken)) {
String username = getUsernameFromToken(refreshToken);
User user = getUserByUsername(username);
return generateToken(user);
}
throw new RuntimeException("无效的刷新令牌");
}
}
常见问题
1. JWT 令牌过期后如何处理?
当 JWT 令牌过期时,客户端需要重新登录或使用刷新令牌获取新的访问令牌。建议实现刷新令牌机制。
// 检查令牌是否即将过期
public boolean isTokenNearExpiration(String token) {
Date expiration = getExpirationDateFromToken(token);
Date now = new Date();
long timeUntilExpiration = expiration.getTime() - now.getTime();
return timeUntilExpiration < 300000; // 5分钟内过期
}
2. 如何撤销 JWT 令牌?
JWT 本身是无状态的,无法直接撤销。可以通过以下方式实现:
- 维护黑名单
- 使用短期令牌 + 刷新令牌
- 在数据库中存储令牌状态
@Service
public class TokenBlacklistService {
private Set<String> blacklist = new HashSet<>();
public void blacklistToken(String token) {
blacklist.add(token);
}
public boolean isBlacklisted(String token) {
return blacklist.contains(token);
}
}
总结
本教程详细介绍了在 Java 中实现 JWT 的完整流程,包括:
- ✅ 环境准备:添加必要的依赖
- ✅ 核心实现:JWT 工具类和服务类
- ✅ 使用示例:Spring Boot 控制器和测试
- ✅ 最佳实践:安全注意事项和性能优化
- ✅ 常见问题:令牌过期和撤销的处理
下一步学习
- 学习 Spring Security 集成 JWT
- 了解微服务架构中的 JWT 使用
- 探索 JWT 的高级特性(如嵌套令牌)
希望这个教程对您有所帮助!如果您有任何问题,欢迎在评论区讨论。