自定义UserDetailsService未被调用问题排查求助
问题解决:自定义UserDetailsService未触发且出现ProviderNotFoundException
问题根源
你遇到的核心问题有两个:
- 重复的UserDetailsService Bean:你的
AuthApiUserDetailsService加了@Service注解,会被Spring自动扫描注册为Bean;同时在WebSecurityConfig里又通过@Bean方法手动注册了一个新的AuthApiUserDetailsService实例。这导致容器中存在两个UserDetailsServiceBean,Spring Security无法确定使用哪一个,进而导致DaoAuthenticationProvider(处理用户名密码认证的核心Provider)无法正确绑定,最终抛出ProviderNotFoundException。 - 密码编码器的隐式依赖问题:你在
loadUserByUsername中使用User.withDefaultPasswordEncoder()创建用户,这是仅用于测试的便捷方法,实际场景中应该显式注册PasswordEncoderBean,确保认证流程能正确匹配密码。
解决方案
步骤1:移除重复的UserDetailsService Bean
选择以下两种方式之一:
- 方式A:保留@Service注解,删除配置类中的@Bean方法
修改WebSecurityConfig,删除重复的userDetailsService()方法:
@Configuration @EnableWebSecurity public class WebSecurityConfig { private static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests((requests) -> requests .requestMatchers("/dashboard").authenticated() .anyRequest().permitAll() ) .formLogin((form) -> form .loginPage("/login") .permitAll() ) .logout((logout) -> logout.permitAll()); return http.build(); } // 移除这个重复的@Bean方法 // @Bean // public UserDetailsService userDetailsService() { // return new AuthApiUserDetailsService(); // } }
- 方式B:移除@Service注解,保留配置类中的@Bean方法
修改AuthApiUserDetailsService,去掉@Service注解:
// 移除@Service注解 public class AuthApiUserDetailsService implements UserDetailsService { private static Logger logger = LoggerFactory.getLogger(AuthApiUserDetailsService.class); @Override public UserDetails loadUserByUsername(String username) { logger.debug("Loading user by username"); if (!"user".equals(username)) { throw new UsernameNotFoundException(username); } logger.debug("Loaded user: " + username); logger.info("This is the testing UserDetails service. Do a real implementation later."); return User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(); } }
步骤2:显式配置PasswordEncoder(推荐生产环境使用)
虽然withDefaultPasswordEncoder()能临时工作,但生产环境建议显式注册PasswordEncoder Bean,并在创建用户时使用该编码器加密密码:
- 在
WebSecurityConfig中添加PasswordEncoderBean:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
- 修改
AuthApiUserDetailsService的loadUserByUsername方法,注入并使用注册的编码器:
@Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) { logger.debug("Loading user by username"); if (!"user".equals(username)) { throw new UsernameNotFoundException(username); } logger.debug("Loaded user: " + username); logger.info("This is the testing UserDetails service. Do a real implementation later."); // 使用显式的PasswordEncoder加密密码 String encodedPassword = passwordEncoder.encode("password"); return User.builder() .username("user") .password(encodedPassword) .roles("USER") .build(); }
步骤3:验证效果
重启应用后,尝试登录,此时loadUserByUsername中的日志应该会正常输出,认证流程也能正常执行。
额外说明
启动日志中的UserDetailsServiceAutoConfiguration提示是正常的,它表示因为你已经自定义了UserDetailsService,所以自动配置不会生效,这符合预期。
内容的提问来源于stack exchange,提问作者gbiz123
相关产品推荐
相关产品推荐

