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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:33:43