JWT认证
2026/1/15大约 3 分钟SpringSpring SecurityJava安全认证授权
JWT认证
JWT 简介
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。它常用于无状态的身份认证。
JWT 结构
# JWT 由三部分组成,用 . 分隔
Header.Payload.Signature
# 示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c添加依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>JWT 工具类
@Component
public class JwtUtils {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration; // 毫秒
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
// 生成 Token
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.claims(claims)
.subject(userDetails.getUsername())
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.compact();
}
// 解析 Token
public Claims parseToken(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
// 获取用户名
public String getUsername(String token) {
return parseToken(token).getSubject();
}
// 验证 Token
public boolean validateToken(String token) {
try {
Claims claims = parseToken(token);
return !claims.getExpiration().before(new Date());
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
// 获取角色
@SuppressWarnings("unchecked")
public List<String> getRoles(String token) {
Claims claims = parseToken(token);
return (List<String>) claims.get("roles");
}
}JWT 认证过滤器
@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 {
// 获取 Token
String token = getTokenFromRequest(request);
if (token != null && jwtUtils.validateToken(token)) {
// 解析用户名
String username = jwtUtils.getUsername(token);
// 加载用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 创建认证对象
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
// 设置到 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}Security 配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 禁用 CSRF(JWT 不需要)
.csrf(csrf -> csrf.disable())
// 禁用 Session(无状态)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 配置权限
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
// 添加 JWT 过滤器
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
// 异常处理
.exceptionHandling(ex -> ex
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}认证入口点
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Map<String, Object> body = new HashMap<>();
body.put("code", 401);
body.put("message", "未授权访问");
body.put("path", request.getServletPath());
new ObjectMapper().writeValue(response.getOutputStream(), body);
}
}登录接口
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
// 认证
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(), request.getPassword()));
// 加载用户信息
UserDetails userDetails = userDetailsService
.loadUserByUsername(request.getUsername());
// 生成 Token
String token = jwtUtils.generateToken(userDetails);
return ResponseEntity.ok(new LoginResponse(token));
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("message", "用户名或密码错误"));
}
}
@PostMapping("/refresh")
public ResponseEntity<?> refresh(@RequestHeader("Authorization") String bearerToken) {
String token = bearerToken.substring(7);
if (jwtUtils.validateToken(token)) {
String username = jwtUtils.getUsername(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
String newToken = jwtUtils.generateToken(userDetails);
return ResponseEntity.ok(new LoginResponse(newToken));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("message", "Token 无效"));
}
}配置文件
# application.yml
jwt:
secret: your-256-bit-secret-key-here-must-be-at-least-32-characters
expiration: 86400000 # 24小时使用示例
# 登录获取 Token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 响应
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
# 使用 Token 访问接口
curl http://localhost:8080/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."小结
JWT 认证是一种无状态的认证方式,适合分布式系统和前后端分离架构。通过自定义过滤器解析 Token 并设置认证信息,实现与 Spring Security 的集成。
面试题预览
常见面试题
- JWT 的结构是怎样的?
- JWT 认证和 Session 认证有什么区别?
- 如何实现 JWT Token 的刷新机制?
