本文目录导读:

- 添加依赖
- JWT工具类
- 用户认证Controller
- JWT认证过滤器
- Spring Security配置
- 受保护的API示例
- 测试示例
- 使用Interceptor方式(不依赖Spring Security)
- 前端调用示例
- 最佳实践建议
我来详细说明如何在Java中实现JWT令牌认证,包含完整的示例代码。
添加依赖
Maven依赖
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
JWT工具类
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
// 密钥(生产环境建议从配置文件中读取)
private static final String SECRET = "YourSuperSecretKeyForJWTTokenGeneration2024!@#$%^&*()";
// 过期时间(24小时)
private static final long EXPIRATION_TIME = 24 * 60 * 60 * 1000;
private static SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(SECRET.getBytes());
}
/**
* 生成JWT令牌
*/
public static String generateToken(String username, Map<String, Object> claims) {
if (claims == null) {
claims = new HashMap<>();
}
return Jwts.builder()
.setClaims(claims) // 自定义声明
.setSubject(username) // 主题(通常是用户名)
.setIssuedAt(new Date()) // 签发时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间
.signWith(getSigningKey(), SignatureAlgorithm.HS256) // 签名算法
.compact();
}
/**
* 解析JWT令牌
*/
public static Claims parseToken(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
} catch (JwtException | IllegalArgumentException e) {
throw new RuntimeException("JWT令牌无效或已过期");
}
}
/**
* 验证令牌是否有效
*/
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
/**
* 从令牌中获取用户名
*/
public static String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
/**
* 检查令牌是否过期
*/
public static boolean isTokenExpired(String token) {
try {
Claims claims = parseToken(token);
return claims.getExpiration().before(new Date());
} catch (Exception e) {
return true;
}
}
}
用户认证Controller
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
// 模拟用户数据库
private static final Map<String, String> USERS = new HashMap<>();
static {
USERS.put("admin", "admin123");
USERS.put("user", "user123");
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
String username = loginRequest.getUsername();
String password = loginRequest.getPassword();
// 验证用户名密码
if (USERS.containsKey(username) && USERS.get(username).equals(password)) {
// 创建自定义声明
Map<String, Object> claims = new HashMap<>();
claims.put("role", username.equals("admin") ? "ADMIN" : "USER");
// 生成JWT令牌
String token = JwtUtil.generateToken(username, claims);
// 返回令牌信息
Map<String, Object> response = new HashMap<>();
response.put("token", token);
response.put("username", username);
response.put("expiresIn", 24 * 60 * 60); // 秒
return ResponseEntity.ok(response);
} else {
return ResponseEntity.status(401).body("用户名或密码错误");
}
}
}
// 登录请求对象
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; }
}
JWT认证过滤器
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = extractTokenFromRequest(request);
if (token != null && JwtUtil.validateToken(token)) {
String username = JwtUtil.getUsernameFromToken(token);
// 创建认证对象
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
username, null, new ArrayList<>());
// 设置认证信息到安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
/**
* 从请求中提取JWT令牌
*/
private String extractTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
Spring Security配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // 禁用CSRF保护
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll() // 认证接口不需要认证
.antMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口
.antMatchers("/api/**").authenticated() // 其他API需要认证
.and()
.addFilterBefore(new JwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class); // 添加JWT过滤器
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
受保护的API示例
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/profile")
public ResponseEntity<?> getProfile(@RequestHeader("Authorization") String token) {
// 从令牌中获取用户信息
String jwtToken = token.replace("Bearer ", "");
String username = JwtUtil.getUsernameFromToken(jwtToken);
Map<String, Object> profile = new HashMap<>();
profile.put("username", username);
profile.put("email", username + "@example.com");
profile.put("createTime", "2024-01-01");
return ResponseEntity.ok(profile);
}
@GetMapping("/admin/users")
public ResponseEntity<?> getAllUsers() {
// 只有管理员可以访问
return ResponseEntity.ok("Admin data");
}
}
测试示例
public class JwtTest {
public static void main(String[] args) {
// 1. 生成令牌
Map<String, Object> claims = new HashMap<>();
claims.put("role", "ADMIN");
claims.put("email", "admin@example.com");
String token = JwtUtil.generateToken("admin", claims);
System.out.println("生成的Token: " + token);
// 2. 验证令牌
boolean isValid = JwtUtil.validateToken(token);
System.out.println("令牌是否有效: " + isValid);
// 3. 解析令牌
Claims parsedClaims = JwtUtil.parseToken(token);
System.out.println("用户名: " + parsedClaims.getSubject());
System.out.println("角色: " + parsedClaims.get("role"));
System.out.println("邮箱: " + parsedClaims.get("email"));
System.out.println("过期时间: " + parsedClaims.getExpiration());
// 4. 获取用户名
String username = JwtUtil.getUsernameFromToken(token);
System.out.println("从Token获取的用户名: " + username);
// 5. 检查是否过期
boolean isExpired = JwtUtil.isTokenExpired(token);
System.out.println("是否过期: " + isExpired);
}
}
使用Interceptor方式(不依赖Spring Security)
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 放行登录请求
if (request.getRequestURI().contains("/api/auth/login")) {
return true;
}
String token = extractToken(request);
if (token == null || !JwtUtil.validateToken(token)) {
response.setStatus(401);
response.getWriter().write("{\"error\": \"未授权访问\"}");
return false;
}
// 将用户信息设置到请求属性中
String username = JwtUtil.getUsernameFromToken(token);
request.setAttribute("username", username);
return true;
}
private String extractToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
前端调用示例
// 登录
async function login() {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'admin',
password: 'admin123'
})
});
const data = await response.json();
const token = data.token;
// 存储token到本地
localStorage.setItem('jwt_token', token);
// 后续请求携带token
const profileResponse = await fetch('/api/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});
const profile = await profileResponse.json();
console.log(profile);
}
最佳实践建议
- 密钥管理:使用密钥管理服务,不要硬编码
- 令牌过期:设置合理的过期时间(通常15-30分钟)
- HTTPS:生产环境必须使用HTTPS
- 刷新机制:实现Token刷新机制
- 黑名单:考虑实现Token黑名单机制
- 日志记录:记录重要的认证事件
这个实现提供了完整的JWT认证流程,你可以根据具体需求进行调整和扩展。