Spring配置双LDAP服务器(非@Primary):按域名路由用户认证
解决Spring中多LDAP服务器动态认证(无@Primary)的问题
我来帮你搞定这个问题!你碰到的“仅允许配置一个ldap-server”的报错,本质是Spring Security默认的LDAP配置逻辑会自动注册一个默认的认证提供者,而且不允许重复注册同一类型的默认提供者。不过咱们完全可以通过自定义认证逻辑绕开这个限制,实现根据用户域名动态选择LDAP服务器的需求,全程不用@Primary注解。
一、Java配置方案(推荐)
我们的思路是:分别配置两个LDAP上下文源→创建对应的LDAP认证提供者→自定义一个复合认证提供者,根据用户名中的域名动态选择对应的LDAP提供者完成认证。
1. 配置两个LDAP上下文源
先为两台LDAP服务器分别创建LdapContextSource,这是连接LDAP的基础配置:
@Bean public LdapContextSource ldap1ContextSource() { LdapContextSource contextSource = new LdapContextSource(); contextSource.setUrl("ldap://ldap1.example.com:389"); contextSource.setUserDn("cn=admin,dc=domain1,dc=com"); contextSource.setPassword("your-ldap1-password"); contextSource.setBase("dc=domain1,dc=com"); return contextSource; } @Bean public LdapContextSource ldap2ContextSource() { LdapContextSource contextSource = new LdapContextSource(); contextSource.setUrl("ldap://ldap2.example.com:389"); contextSource.setUserDn("cn=admin,dc=domain2,dc=com"); contextSource.setPassword("your-ldap2-password"); contextSource.setBase("dc=domain2,dc=com"); return contextSource; }
2. 创建对应的LDAP认证提供者
为每个上下文源绑定一个LdapAuthenticationProvider,这里用绑定认证(BindAuthenticator)作为示例,你也可以根据需求改用密码比对认证:
@Bean public AuthenticationProvider ldap1AuthProvider(LdapContextSource ldap1ContextSource) { // 配置绑定认证器,适配ldap1的用户DN规则 BindAuthenticator authenticator = new BindAuthenticator(ldap1ContextSource); authenticator.setUserDnPatterns(new String[]{"uid={0},ou=users"}); // 请根据你的LDAP结构调整 LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator); // 如果需要获取用户组权限,可以添加LdapAuthoritiesPopulator // provider.setAuthoritiesPopulator(new DefaultLdapAuthoritiesPopulator(ldap1ContextSource, "ou=groups")); return provider; } @Bean public AuthenticationProvider ldap2AuthProvider(LdapContextSource ldap2ContextSource) { BindAuthenticator authenticator = new BindAuthenticator(ldap2ContextSource); authenticator.setUserDnPatterns(new String[]{"uid={0},ou=users"}); // 适配ldap2的结构 LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator); return provider; }
3. 自定义域名路由的复合认证提供者
核心逻辑在这里:提取用户名中的域名,动态选择对应的LDAP提供者完成认证:
@Component public class DomainBasedLdapAuthProvider implements AuthenticationProvider { private final AuthenticationProvider ldap1AuthProvider; private final AuthenticationProvider ldap2AuthProvider; // 构造注入两个LDAP认证提供者 public DomainBasedLdapAuthProvider(AuthenticationProvider ldap1AuthProvider, AuthenticationProvider ldap2AuthProvider) { this.ldap1AuthProvider = ldap1AuthProvider; this.ldap2AuthProvider = ldap2AuthProvider; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String credentials = (String) authentication.getCredentials(); // 处理用户名格式:假设是 user@domain1 或 user@domain2 if (username.endsWith("@domain1")) { // 去掉域名后缀,传给ldap1认证 String ldapUsername = username.replace("@domain1", ""); return ldap1AuthProvider.authenticate( new UsernamePasswordAuthenticationToken(ldapUsername, credentials) ); } else { // 默认用ldap2认证(可以扩展判断@domain2的逻辑) String ldapUsername = username.replace("@domain2", ""); return ldap2AuthProvider.authenticate( new UsernamePasswordAuthenticationToken(ldapUsername, credentials) ); } } @Override public boolean supports(Class<?> authentication) { // 只处理用户名密码认证 return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } }
4. 注册自定义认证提供者到SecurityFilterChain
最后在Spring Security的配置中,注册我们的复合认证提供者:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, DomainBasedLdapAuthProvider customAuthProvider) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(form -> form.permitAll()) .authenticationProvider(customAuthProvider); // 替换默认的LDAP认证提供者 return http.build(); }
二、XML配置方案
如果你的项目仍在使用XML配置,思路和Java配置一致,只是用XML语法实现:
1. 配置两个LDAP上下文源
<bean id="ldap1ContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> <constructor-arg value="ldap://ldap1.example.com:389/dc=domain1,dc=com"/> <property name="userDn" value="cn=admin,dc=domain1,dc=com"/> <property name="password" value="your-ldap1-password"/> </bean> <bean id="ldap2ContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> <constructor-arg value="ldap://ldap2.example.com:389/dc=domain2,dc=com"/> <property name="userDn" value="cn=admin,dc=domain2,dc=com"/> <property name="password" value="your-ldap2-password"/> </bean>
2. 创建对应的LDAP认证提供者
<bean id="ldap1AuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> <constructor-arg> <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> <constructor-arg ref="ldap1ContextSource"/> <property name="userDnPatterns"> <list> <value>uid={0},ou=users</value> </list> </property> </bean> </constructor-arg> </bean> <bean id="ldap2AuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> <constructor-arg> <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> <constructor-arg ref="ldap2ContextSource"/> <property name="userDnPatterns"> <list> <value>uid={0},ou=users</value> </list> </property> </bean> </constructor-arg> </bean>
3. 注册自定义复合认证提供者
<!-- 注意:自定义的DomainBasedLdapAuthProvider需要和Java配置中的实现类一致 --> <bean id="domainBasedAuthProvider" class="com.yourpackage.DomainBasedLdapAuthProvider"> <constructor-arg ref="ldap1AuthProvider"/> <constructor-arg ref="ldap2AuthProvider"/> </bean>
4. 配置认证管理器
<security:authentication-manager> <security:authentication-provider ref="domainBasedAuthProvider"/> </security:authentication-manager>
关键注意事项
- 用户名格式适配:如果你的用户名格式是
domain1\user而不是user@domain1,请修改authenticate方法中的域名提取逻辑(比如用username.split("\\\\")分割)。 - LDAP结构适配:请根据实际的LDAP目录结构调整
userDnPatterns或userSearchFilter,确保能正确找到用户。 - 权限扩展:如果需要获取用户的LDAP组权限,可以为每个
LdapAuthenticationProvider配置LdapAuthoritiesPopulator。
内容的提问来源于stack exchange,提问作者iPhoneJavaDev
相关产品推荐
相关产品推荐

