Spring Security 5.7如何配置LDAP认证同时保留JDBC表单登录
问题根因
Spring Security 5.7+ 组件化配置中,直接调用http.authenticationManager()传入单个认证管理器,会完全覆盖默认支持多认证源轮询的ProviderManager实现,不会自动合并JDBC、LDAP等不同认证逻辑,这就是两种认证方式无法同时生效的核心原因。
旧版WebSecurityConfigurerAdapter配置下,先后调用auth.userDetailsService()和auth.ldapAuthentication()时,框架会自动按顺序把JDBC对应的DaoAuthenticationProvider、LDAP对应的LdapAuthenticationProvider注册到同一个ProviderManager中,按添加顺序轮询实现认证回退。
正确配置方案
不需要手动构建全局AuthenticationManager,直接将两个认证源对应的AuthenticationProvider按优先级注册到Spring容器即可,默认的ProviderManager会自动收集所有Provider按顺序执行,实现LDAP优先、失败回退JDBC的逻辑。
完整配置代码如下:
@Configuration public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // @formatter:off http.authorizeRequests() .mvcMatchers("/services/**", "/resources/**", "/webjars/**").permitAll() .anyRequest().authenticated(); http.httpBasic(); http.formLogin().permitAll() .loginPage("/login") .defaultSuccessUrl("/customer/overview", true); http.logout().permitAll(); http.csrf().disable(); http.headers().frameOptions().disable(); // 移除手动设置authenticationManager的代码,避免覆盖默认多Provider管理器 // @formatter:on return http.build(); } /** * LDAP认证Provider,优先级最高 */ @Bean @Order(0) public LdapAuthenticationProvider ldapAuthenticationProvider( BaseLdapPathContextSource ldapContextSource, UserDetailsService userDetailsService) { // 配置LDAP用户搜索规则,和旧版配置对齐 BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource); authenticator.setUserSearch(new FilterBasedLdapUserSearch( "ou=people", "(uid={0})", ldapContextSource )); // 配置权限映射 UserDetailsServiceLdapAuthoritiesPopulator authoritiesPopulator = new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService); LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(authenticator, authoritiesPopulator); // 如果需要使用自定义的用户上下文映射,直接在此处设置即可,和旧版customLdapUserDetailsContextMapper逻辑一致 // ldapProvider.setUserDetailsContextMapper(customLdapUserDetailsContextMapper()); return ldapProvider; } /** * JDBC表单认证Provider,LDAP认证失败后回退执行 */ @Bean @Order(1) public DaoAuthenticationProvider jdbcAuthenticationProvider( UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider(); daoProvider.setUserDetailsService(userDetailsService); daoProvider.setPasswordEncoder(passwordEncoder); // 关闭用户不存在隐藏逻辑,保证LDAP查无用户时可正常回退到JDBC认证 daoProvider.setHideUserNotFoundExceptions(false); return daoProvider; } @Bean CustomLdapUserDetailsContextMapper customLdapUserDetailsContextMapper(UserDetailsService userDetailsService) { CustomLdapUserDetailsContextMapper mapper = new CustomLdapUserDetailsContextMapper(); mapper.setCustomUserDetailsService(userDetailsService); return mapper; } }
配置说明
- 认证执行顺序完全符合预期:收到登录请求后先尝试LDAP绑定认证,认证成功直接返回结果;如果LDAP查无用户、密码错误导致认证失败,自动回退到JDBC表单认证逻辑。
- 无需额外定义
LdapAuthenticationManagerFactory相关Bean,直接注册Provider的方式更贴合Spring Security 5.7+的组件化设计,和旧版配置的执行逻辑完全一致。 - 如果项目中自定义了
PasswordEncoder,直接注入到DaoAuthenticationProvider即可,没有自定义的话注入Spring Boot自动配置的PasswordEncoder实例就行。
内容的提问来源于stack exchange,提问作者Ralan
相关产品推荐
相关产品推荐

