【BUG记录】SpringSecurity6.2以上 AuthenticationManager=null
个人博客接入ai大模型
部分堆栈信息
java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AuthenticationManager.authenticate(org.springframework.security.core.Authentication)" because the return value of "com.eleventh.oneblog.jwt.filter.JwtAuthenticationFilter.getAuthenticationManager()" is null
参考文章: Spring Security authenticationManager()返回null,必须定义authenticationManagerBean的原因分析
该错误的根本原因还是在于显式设置 AuthenticationManager 到 HttpSecurity。
参考其他文章并没有指出具体设置位置。在多模块开发下,我的springSecurity的执行链为WebSecurityConfig->TokenAuthenticationFilter->jwtAuthenticationSecurityConfig->JwtAuthenticationFilter。而最开始我将AuthenticationManager显式设置在WebSecurityConfig但执行结果同样报错
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// 显式获取并设置 AuthenticationManager
AuthenticationManager authManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));
http.authenticationManager(authManager);
// 先配置其他安全设置
http
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(basic -> basic.authenticationEntryPoint(authEntryPoint))
.exceptionHandling(exception -> exception.accessDeniedHandler(deniedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").authenticated()
.anyRequest().permitAll()
)
.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// 最后配置 JWT 安全模块
jwtAuthenticationSecurityConfig.configure(http);
return http.build();
}
原因是咋多模块开发下,将jwt安全认证与springSecurity分开在不同模块,同时关键类SecurityConfigurerAdapter并没有在WebSecurityConfig继承。SecurityConfigurerAdapter继承在jwtAuthenticationSecurityConfig中,所以AuthenticationManager显式设置也应该在该配置类中
@Configuration
public class JwtAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private RestAuthenticationSuccessHandler restAuthenticationSuccessHandler;
@Autowired
private RestAuthenticationFailureHandler restAuthenticationFailureHandler;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
// 先设置 AuthenticationManager
AuthenticationManager authManager = authenticationManager(
httpSecurity.getSharedObject(AuthenticationConfiguration.class)
);
// httpSecurity.authenticationManager(authManager);
// 自定义的用于 JWT 身份验证的过滤器
JwtAuthenticationFilter filter = new JwtAuthenticationFilter();
filter.setAuthenticationManager(authManager);
// 设置登录认证对应的处理类(成功处理、失败处理)
filter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(restAuthenticationFailureHandler);
// 直接使用 DaoAuthenticationProvider, 它是 Spring Security 提供的默认的身份验证提供者之一
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
// 设置 userDetailService,用于获取用户的详细信息
provider.setUserDetailsService(userDetailsService);
// 设置加密算法
provider.setPasswordEncoder(passwordEncoder);
httpSecurity.authenticationProvider(provider);
// 将这个过滤器添加到 UsernamePasswordAuthenticationFilter 之前执行
httpSecurity.addFilterAfter(filter, UsernamePasswordAuthenticationFilter.class);
}
}
总结
所以AuthenticationManager显式设置需要在继承了SecurityConfigurerAdapter的类中。