Spring Security与JWT配置问题求助(Spring Boot 2.7.10)
解决Spring Boot 2.7.10中addFilter无法接受自定义JWT过滤器的问题
核心原因
你的问题根源在于AuthenticationManager的获取方式不符合Spring Boot 2.7.x的规范,同时WebSecurityConfigurerAdapter在该版本中的内部逻辑已调整,这也是同一份代码在其他项目能运行的可能原因(大概率其他项目版本不同)。直接调用父类的authenticationManager()无法正确生成可用的实例,导致自定义过滤器初始化失败。
解决方案
1. 正确暴露AuthenticationManager为Bean
在SecurityConfig中添加专门的Bean方法暴露AuthenticationManager,替代直接调用父类方法:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${jwt.secret}") private String jwtSecret; @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } // 暴露AuthenticationManager为可注入的Bean @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { AuthenticationManager authManager = authenticationManagerBean(); http.cors().and().authorizeRequests() .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authManager)) .addFilter(new JwtAuthorizationFilter(authManager)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }
2. 修正JwtAuthenticationFilter的硬编码问题
你的过滤器中使用了硬编码的SECRET和EXPIRATION_TIME,建议替换为配置注入的参数,避免环境不一致问题:
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final String jwtSecret; private final long expirationTime; // 通过构造器注入配置参数 public JwtAuthenticationFilter(AuthenticationManager authenticationManager, String jwtSecret, long expirationTime) { this.authenticationManager = authenticationManager; this.jwtSecret = jwtSecret; this.expirationTime = expirationTime; setFilterProcessesUrl("/api/services/controller/user/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException { try { User creds = new ObjectMapper() .readValue(req.getInputStream(), User.class); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( creds.getUsername(), creds.getPassword(), new ArrayList<>()) ); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException { String token = JWT.create() .withSubject(((User) auth.getPrincipal()).getUsername()) .withExpiresAt(new Date(System.currentTimeMillis() + expirationTime)) .sign(Algorithm.HMAC512(jwtSecret.getBytes())); String body = ((User) auth.getPrincipal()).getUsername() + " " + token; res.getWriter().write(body); res.getWriter().flush(); } }
3. 检查JwtAuthorizationFilter的继承类
确保JwtAuthorizationFilter继承自OncePerRequestFilter(这是处理授权逻辑的标准父类),示例实现如下:
public class JwtAuthorizationFilter extends OncePerRequestFilter { private final AuthenticationManager authenticationManager; private final String jwtSecret; public JwtAuthorizationFilter(AuthenticationManager authenticationManager, String jwtSecret) { this.authenticationManager = authenticationManager; this.jwtSecret = jwtSecret; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String header = request.getHeader("Authorization"); if (header == null || !header.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } UsernamePasswordAuthenticationToken authentication = getAuthentication(request); SecurityContextHolder.getContext().setAuthentication(authentication); filterChain.doFilter(request, response); } private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { String token = request.getHeader("Authorization").replace("Bearer ", ""); if (token != null) { String user = JWT.require(Algorithm.HMAC512(jwtSecret.getBytes())) .build() .verify(token) .getSubject(); if (user != null) { return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); } return null; } return null; } }
4. 可选:替换废弃的WebSecurityConfigurerAdapter
Spring Boot 2.7+已废弃WebSecurityConfigurerAdapter,推荐使用基于组件的配置方式,避免后续版本兼容性问题:
@Configuration @EnableWebSecurity public class SecurityConfig { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration-time}") private long expirationTime; @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception { http.cors().and().authorizeRequests() .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authManager, jwtSecret, expirationTime)) .addFilter(new JwtAuthorizationFilter(authManager, jwtSecret)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); return http.build(); } }
额外检查点
- 确认两个项目的Spring Boot及Spring Security依赖版本完全一致
- 检查自定义过滤器所在包是否被Spring正确扫描
- 验证配置文件中的
jwt.secret等参数是否正确注入
内容的提问来源于stack exchange,提问作者Dhia Ammar
相关产品推荐
相关产品推荐

