升级Spring Boot 2.7替换WebSecurityConfigurerAdapter遇认证异常
我正尝试将应用升级至Spring Boot 2.7版本,该版本对应的Spring Security为5.7,且WebSecurityConfigurerAdapter已被弃用。
原有可正常运行的代码(基于WebSecurityConfigurerAdapter)
@Configuration @Profile("local-auth") class LocalAuth { @Bean public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter( PasswordEncoder passwordEncoder) { return new WebSecurityConfigurerAdapter() { @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("testuser") .password(passwordEncoder.encode("a")) .authorities(toAuthorities(List.of("SOME_AUTHORITY"))); } private List<? extends GrantedAuthority> toAuthorities(List<String> roles) { return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/login", "/actuator/**", "/publico/**").permitAll() .anyRequest().authenticated() ) .formLogin(formLogin -> formLogin.defaultSuccessUrl("http://localhost:4200/app/").permitAll()) .logout(logout -> logout.logoutUrl("/logout")) .csrf(AbstractHttpConfigurer::disable); } }; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
替换后的问题
按照官方指南改用InMemoryUserDetailsManager后,登录表单可正常显示,但登录时提示No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken,日志无详细错误输出;尝试手动配置AuthenticationManager Bean时,应用启动报错Cannot apply org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer@313c3cb to already built object。
正确的替换方案
核心问题是密码格式未符合Spring Security 5的规范,且未遵循新的组件式配置风格,以下是修正后的完整代码:
@Configuration @Profile("local-auth") class LocalAuth { @Bean public InMemoryUserDetailsManager userDetailsService() { // 密码需携带加密算法前缀,让框架识别对应的PasswordEncoder UserDetails user = User.withUsername("testuser") .password("{bcrypt}" + new BCryptPasswordEncoder().encode("a")) .authorities(toAuthorities(List.of("SOME_AUTHORITY"))) .build(); return new InMemoryUserDetailsManager(user); } private List<? extends GrantedAuthority> toAuthorities(List<String> roles) { return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // 替换过时的authorizeRequests为authorizeHttpRequests .authorizeHttpRequests(auth -> auth .requestMatchers("/login", "/actuator/**", "/publico/**").permitAll() .anyRequest().authenticated() ) .formLogin(form -> form .defaultSuccessUrl("http://localhost:4200/app/") .permitAll() ) .logout(logout -> logout .logoutUrl("/logout") ) .csrf(csrf -> csrf.disable()); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
关键修正说明
密码格式规范:
Spring Security 5要求密码必须携带加密算法前缀(如{bcrypt}),框架才能自动匹配对应的PasswordEncoder进行验证。之前的代码仅存储了加密后的密码但无前缀,导致无法识别加密类型,进而找不到对应的AuthenticationProvider。替换过时API:
authorizeRequests()已被标记为过时,改用authorizeHttpRequests(),契合Spring Security 5.7的新配置风格。避免手动构建AuthenticationManager:
当声明了UserDetailsService和PasswordEncoder的Bean后,Spring Security会自动配置对应的AuthenticationManager和AuthenticationProvider,手动构建会破坏自动配置流程,导致启动报错。
内容的提问来源于stack exchange,提问作者ekalin

