本文目录导读:

我将为你详细讲解Spring Security集成JWT令牌认证的完整实现方案。
项目依赖配置
在pom.xml中添加必要的依赖:
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Web -->
<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.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>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
JWT工具类
@Component
@Slf4j
public class JwtUtils {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpirationMs;
// 生成JWT令牌
public String generateJwtToken(String username, List<String> roles) {
return Jwts.builder()
.setSubject(username)
.claim("roles", roles)
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
// 从JWT中提取用户名
public String getUserNameFromJwtToken(String token) {
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
// 验证JWT令牌
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
log.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
log.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
log.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
log.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
log.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
}
认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
return null;
}
}
Security配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
认证入口点
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String json = String.format(
"{\"error\": \"%s\", \"message\": \"%s\"}",
"Unauthorized",
authException.getMessage()
);
response.getOutputStream().println(json);
}
}
控制器示例
认证控制器
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
String jwt = jwtUtils.generateJwtToken(userDetails.getUsername(), roles);
return ResponseEntity.ok(new JwtResponse(
jwt,
userDetails.getUsername(),
roles
));
}
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody SignupRequest signupRequest) {
// 实现用户注册逻辑
return ResponseEntity.ok("User registered successfully");
}
}
受保护的API控制器
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/user/profile")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public ResponseEntity<?> getUserProfile() {
// 获取当前登录用户信息
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
return ResponseEntity.ok("User: " + userDetails.getUsername());
}
@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> getAdminDashboard() {
return ResponseEntity.ok("Admin Dashboard Data");
}
}
请求/响应模型
@Data
public class LoginRequest {
@NotBlank
private String username;
@NotBlank
private String password;
}
@Data
public class SignupRequest {
@NotBlank
@Size(min = 3, max = 20)
private String username;
@NotBlank
@Size(min = 6, max = 40)
private String password;
}
@Data
@AllArgsConstructor
public class JwtResponse {
private String token;
private String username;
private List<String> roles;
}
配置文件 (application.yml)
jwt:
secret: your-secret-key-here-make-it-long-and-secure
expiration: 86400000 # 24 hours in milliseconds
server:
port: 8080
spring:
application:
name: jwt-security-demo
使用示例
登录获取令牌
POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "password123"
}
// 响应
{
"token": "eyJhbGciOiJIUzUxMiJ9...",
"username": "admin",
"roles": ["ROLE_ADMIN"]
}
使用令牌访问保护资源
GET /api/user/profile Authorization: Bearer eyJhbGciOiJIUzUxMiJ9...
刷新令牌机制(扩展)
@Component
public class RefreshTokenService {
@Value("${jwt.refresh-expiration}")
private int refreshTokenDurationMs;
public RefreshToken createRefreshToken(Long userId) {
RefreshToken refreshToken = new RefreshToken();
refreshToken.setUserId(userId);
refreshToken.setToken(UUID.randomUUID().toString());
refreshToken.setExpiryDate(new Date((new Date()).getTime() + refreshTokenDurationMs));
// 保存到数据库
refreshTokenRepository.save(refreshToken);
return refreshToken;
}
public RefreshToken verifyRefreshToken(String token) {
RefreshToken refreshToken = refreshTokenRepository.findByToken(token)
.orElseThrow(() -> new RuntimeException("Refresh token not found"));
if (refreshToken.getExpiryDate().before(new Date())) {
refreshTokenRepository.delete(refreshToken);
throw new RuntimeException("Refresh token expired");
}
return refreshToken;
}
}
这个完整的Spring Security + JWT集成方案提供了:
- ✅ 无状态认证
- ✅ 令牌生成和验证
- ✅ 安全的密码加密
- ✅ 角色权限控制
- ✅ 可扩展的刷新令牌机制
- ✅ 良好的异常处理
你可以根据实际需求调整令牌过期时间、添加刷新令牌机制或集成OAuth2等高级功能。