求助:解决AuthenticationFilter中authenticationManager为null的问题
错误信息
原因:org.springframework.beans.factory.BeanCreationException: 创建文件[mypath\security\filter\AuthenticationFilter.class]中定义的名称为'authenticationFilter'的Bean时出错:必须指定authenticationManager
.
.原因:java.lang.IllegalArgumentException: 必须指定authenticationManager
问题描述
之前解决了一个循环依赖错误,现在出现新问题:AuthenticationFilter中的authenticationManager bean为null,怀疑循环依赖未彻底解决,或存在其他配置问题。
相关代码
Security Config代码
@Configuration public class SecurityConfig { @Autowired private AuthenticationFilter authenticationFilter; /*** * This method is used to configure the security filter chain * @param http * @return * @throws Exception */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { authenticationFilter.setFilterProcessesUrl("/user/login"); http .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) //h2-console fix .csrf(AbstractHttpConfigurer::disable) .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(c -> c .requestMatchers("/user/login", "/user/register", "/h2-console", "/h2-console/*").permitAll() .anyRequest().authenticated()) .addFilterBefore(new ExceptionHandlerFilter(), AuthenticationFilter.class) .addFilter(authenticationFilter); return http.build(); } }
AuthenticationFilter代码
@Component public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Autowired private AuthenticationManager authenticationManager; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try{ User user = new ObjectMapper().readValue(request.getInputStream(), User.class); Authentication authentication = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()); return authenticationManager.authenticate(authentication); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { super.unsuccessfulAuthentication(request, response, failed); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { super.successfulAuthentication(request, response, chain, authResult); } }
CustomAuthenticationManager代码
@Component public class CustomAuthenticationManager implements AuthenticationManager { @Autowired private UserService userService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { User user = userService.getUser(authentication.getName()); if(!userService.validatePassword(authentication.getCredentials().toString(), user.getPassword())){ throw new BadCredentialsException("Invalid password"); } return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials()); } }
UserServiceImpl代码
@Service public class UserServiceImpl implements UserService{ @Autowired private static BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired private UserRepository userRepository; @Autowired private ModelMapper modelMapper; @Override public UserDto saveUser(UserDto userDto) { userDto.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); return convertToDto(userRepository.save(convertToEntity(userDto))); } @Override public User getUser(String email){ Optional<User> user = userRepository.findByEmail(email); return unwrapUser(user); } public boolean validatePassword(String password, String encodedPassword){ return bCryptPasswordEncoder.matches(password, encodedPassword); } static User unwrapUser(Optional<User> user) { if(user.isPresent()){ return user.get(); } else throw new EntityNotFoundException("User not found"); } private UserDto convertToDto(User user) { return modelMapper.map(user, UserDto.class); } private User convertToEntity(UserDto userDto){ return modelMapper.map(userDto, User.class); } }
问题解答
1. 是否存在循环依赖?
目前代码的依赖链为:AuthenticationFilter → CustomAuthenticationManager → UserService → UserRepository/ModelMapper/BCryptPasswordEncoder,没有形成闭环,不存在循环依赖。
2. authenticationManager为null的原因
核心问题出在AuthenticationFilter的字段定义与父类的冲突:
- 父类
UsernamePasswordAuthenticationFilter(继承自AbstractAuthenticationProcessingFilter)本身有一个protected AuthenticationManager authenticationManager字段,并且实现了InitializingBean接口,在afterPropertiesSet方法中会检查该字段是否为null,若为null则抛出你遇到的异常。 - 你在子类
AuthenticationFilter中重新声明了private AuthenticationManager authenticationManager字段并注入,导致父类的字段未被赋值,而Spring Security的初始化逻辑检查的是父类的字段,因此触发异常。
另外,UserServiceImpl中还有一个隐藏问题:静态字段无法被@Autowired注入,static BCryptPasswordEncoder会始终为null,后续保存用户时会触发空指针异常。
3. 修复方案
方案一:修改AuthenticationFilter,正确注入AuthenticationManager
去掉子类中重复的字段,改用构造函数注入并赋值给父类字段:
@Component public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { // 去掉子类自己的authenticationManager字段 public AuthenticationFilter(AuthenticationManager authenticationManager) { // 调用父类的setter方法赋值 super.setAuthenticationManager(authenticationManager); // 可以在这里直接设置filterProcessesUrl,不用在SecurityConfig里设置 super.setFilterProcessesUrl("/user/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try{ User user = new ObjectMapper().readValue(request.getInputStream(), User.class); Authentication authentication = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()); // 调用父类的getter获取authenticationManager return getAuthenticationManager().authenticate(authentication); } catch (IOException e) { throw new RuntimeException(e); } } // 其他方法保持不变 }
方案二:修复UserServiceImpl的静态字段问题
去掉BCryptPasswordEncoder的static修饰符,并确保Spring容器中有该Bean:
@Service public class UserServiceImpl implements UserService{ @Autowired private BCryptPasswordEncoder bCryptPasswordEncoder; // 去掉static // 其他代码不变 }
在SecurityConfig中添加BCryptPasswordEncoder的Bean定义:
@Configuration public class SecurityConfig { @Autowired private AuthenticationFilter authenticationFilter; // 添加这个Bean @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } // filterChain方法不变 }
调试此类错误的技巧
- 查看Bean初始化日志:开启
org.springframework.beans.factory包的DEBUG级别日志,能清晰看到每个Bean的创建顺序、依赖注入过程,定位哪个环节出现问题。 - 检查父类字段冲突:继承Spring框架组件(如Security过滤器)时,避免重新声明父类已有的字段,否则会覆盖或绕过框架的初始化逻辑。
- 优先使用构造函数注入:构造函数注入能确保Bean创建时依赖已完全注入,避免字段注入导致的延迟初始化问题,也更容易发现依赖缺失。
- 利用InitializingBean做自检:自定义Bean可实现
InitializingBean接口,在afterPropertiesSet方法中检查关键依赖是否为null,提前暴露问题。 - 跟踪Spring Security初始化逻辑:Spring Security的过滤器有严格的初始化要求,比如必须设置AuthenticationManager,可查看父类源码了解具体校验逻辑。
内容的提问来源于stack exchange,提问作者Fábio

