在现代 Web 应用开发中,无状态的身份认证方案越来越受到青睐。JWT(JSON Web Token) 作为一种轻量级的认证机制,配合 Spring Security 使用,可以构建出高效、可扩展的安全认证系统。本文将详细介绍如何在 Spring Boot 4 项目中集成 Spring Security 6 实现基于 JWT 的登录认证。
一、项目依赖配置
首先,在 pom.xml 中添加必要的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JWT 依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
</dependencies>
二、JWT 工具类实现
创建 JWT 工具类,负责 Token 的生成和解析:
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration:86400000}")
private long expiration;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.subject(username)
.issuedAt(now)
.expiration(expiryDate)
.signWith(getSigningKey())
.compact();
}
public String extractUsername(String token) {
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token);
return true;
} catch (Exception e) {
return false;
}
}
}
三、Security 配置类
配置 Spring Security,禁用 Session,添加 JWT 过滤器:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login").permitAll()
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
四、JWT 认证过滤器
创建过滤器拦截请求,验证 JWT Token:
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String token = authHeader.substring(7);
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.extractUsername(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
五、登录接口实现
创建登录控制器,验证用户凭据并返回 JWT:
@RestController
@RequestMapping("/api")
public class LoginController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(), request.getPassword())
);
String token = jwtUtil.generateToken(request.getUsername());
return ResponseEntity.ok(Map.of(
"code", 0,
"message", "登录成功",
"data", Map.of("token", token)
));
} catch (BadCredentialsException e) {
return ResponseEntity.status(401).body(Map.of(
"code", 401,
"message", "用户名或密码错误"
));
}
}
}
六、配置文件
在 application.yml 中添加 JWT 配置:
jwt:
secret: your-256-bit-secret-key-here-must-be-at-least-32-characters
expiration: 86400000 # 24小时,单位毫秒
七、测试验证
启动应用后,可以使用以下流程测试:
- 发送 POST 请求到 /api/login 获取 Token
- 在后续请求的请求头中添加 Authorization: Bearer {token}
- 访问受保护的接口,验证认证是否生效
总结
通过以上步骤,我们完成了 Spring Boot 4 与 Spring Security 6 的 JWT 集成。这种无状态的认证方案非常适合微服务架构和分布式系统,可以有效降低服务端的会话管理负担。在实际生产环境中,建议添加 Token 刷新机制、黑名单等增强功能。
本文示例代码基于 Spring Boot 4.0.3 和 Spring Security 6.2.0,不同版本可能存在 API 差异,请根据实际情况调整。