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

自定义UserDetailsService未被调用问题排查求助

问题解决:自定义UserDetailsService未触发且出现ProviderNotFoundException

问题根源

你遇到的核心问题有两个:

  1. 重复的UserDetailsService Bean:你的AuthApiUserDetailsService加了@Service注解,会被Spring自动扫描注册为Bean;同时在WebSecurityConfig里又通过@Bean方法手动注册了一个新的AuthApiUserDetailsService实例。这导致容器中存在两个UserDetailsService Bean,Spring Security无法确定使用哪一个,进而导致DaoAuthenticationProvider(处理用户名密码认证的核心Provider)无法正确绑定,最终抛出ProviderNotFoundException。
  2. 密码编码器的隐式依赖问题:你在loadUserByUsername中使用User.withDefaultPasswordEncoder()创建用户,这是仅用于测试的便捷方法,实际场景中应该显式注册PasswordEncoder Bean,确保认证流程能正确匹配密码。

解决方案

步骤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,并在创建用户时使用该编码器加密密码:

  1. 在WebSecurityConfig中添加PasswordEncoder Bean:
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:55:04