You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 22:57:02