如何基于Spring Security 6实现JWT Bearer Token验证的SecurityConfig
Spring Security 6 实现JWT Bearer Token验证完整步骤
1. 修复SecurityConfig核心配置
你的代码注释掉了添加JwtTokenFilter的关键逻辑,且Spring Security 6推荐使用Lambda风格配置,同时需补充未授权处理器、密码编码器等核心Bean。调整后的完整配置如下:
@Configuration @EnableWebSecurity @RequiredArgsConstructor // 替代@Autowired,代码更简洁 public class SecurityConfig { private final JwtTokenFilter jwtTokenFilter; private final AuthenticationEntryPoint unauthorizedHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/authenticate").permitAll() .anyRequest().authenticated() ) .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); // 必须启用该过滤器 return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } }
2. 实现JwtTokenFilter请求过滤器
负责从Authorization: Bearer <token>请求头中提取Token,验证有效性并将认证信息存入Security上下文:
@Component @RequiredArgsConstructor public class JwtTokenFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); String token = null; String username = null; if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); username = jwtUtil.extractUsername(token); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }
3. 实现JwtUtil工具类
封装Token生成、解析、验证逻辑:
@Component public class JwtUtil { // *生产环境不要硬编码密钥,存入配置文件或密钥管理服务* private final String SECRET_KEY = "your-safe-secret-key-here"; private final long JWT_EXPIRATION = 86400000L; // 24小时有效期 public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parserBuilder() .setSigningKey(getSignKey()) .build() .parseClaimsJws(token) .getBody(); } private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, userDetails.getUsername()); } private String createToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION)) .signWith(getSignKey(), SignatureAlgorithm.HS256) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private Key getSignKey() { byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); return Keys.hmacShaKeyFor(keyBytes); } }
4. 实现自定义UserDetailsService
从数据源加载用户信息,需与你的用户存储逻辑适配:
@Service @RequiredArgsConstructor public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户不存在:" + username)); return User.builder() .username(user.getUsername()) .password(user.getPassword()) .authorities(user.getRoles().stream() .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList())) .build(); } }
5. 实现未授权统一处理器(可选但推荐)
处理Token无效/缺失时的统一响应:
@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); final Map<String, Object> body = new HashMap<>(); body.put("status", HttpServletResponse.SC_UNAUTHORIZED); body.put("error", "未授权"); body.put("message", authException.getMessage()); body.put("path", request.getServletPath()); final ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getOutputStream(), body); } }
6. 编写登录接口生成Token
验证用户密码后返回JWT Token:
@RestController @RequestMapping("/api") @RequiredArgsConstructor public class AuthController { private final AuthenticationManager authenticationManager; private final JwtUtil jwtUtil; private final UserDetailsService userDetailsService; @PostMapping("/authenticate") public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authRequest) throws Exception { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); } catch (BadCredentialsException e) { throw new Exception("用户名或密码错误", e); } final UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername()); final String jwt = jwtUtil.generateToken(userDetails); return ResponseEntity.ok(new AuthResponse(jwt)); } // 请求响应辅助类 public static class AuthRequest { private String username; private String password; // getter、setter } public static class AuthResponse { private String jwt; public AuthResponse(String jwt) { this.jwt = jwt; } // getter } }
关键注意事项
- 生产环境中,JWT密钥、有效期需通过配置文件注入,禁止硬编码
- 用户密码必须通过
PasswordEncoder加密后存储 - 可根据业务需求添加Token刷新机制,优化用户体验
内容的提问来源于stack exchange,提问作者Rahul Kavati
相关产品推荐
相关产品推荐

