0

我正在尝试在拆分域环境中将辅助 ldap contextSource 添加到 spring 安全性,但我似乎做得不够。我意识到以前也有人问过类似的问题,但这是针对登录同一个应用程序的两个单独的域。

我的第一步是将辅助上下文源添加到我的 security-config xml 文件中,如下所示:

<beans:bean id="secondaryContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <beans:constructor-arg value="ldap://<ldap address>:389/DC=example,DC=com"/>
    <beans:property name="userDn" value="CN=BindAccount,CN=Users,DC=example,DC=com" />
    <beans:property name="password" value="examplepw" />
</beans:bean>

此外,我将构造函数参数添加到 ldapAuthenticationProvider 并用我的自定义类替换了 BindAuthenticator,如下所示:

    <beans:bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">    
    <beans:constructor-arg>
        <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <beans:constructor-arg ref="contextSource" />
            <beans:constructor-arg ref="secondaryContextSource" />
            <beans:property name="userSearch">
                <beans:bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
                  <beans:constructor-arg index="0" value="CN=Users"/>
                  <beans:constructor-arg index="1" value="(userPrincipalName={0})"/>
                  <beans:constructor-arg index="2" ref="contextSource" />
                </beans:bean>
            </beans:property>

        </beans:bean>
    </beans:constructor-arg>       
     <beans:property name="userDetailsContextMapper">
        <beans:bean id="employeeServiceFacade" class="com.example.service.security.EmployeeServiceFacade" />
    </beans:property>   
      <beans:constructor-arg>
        <beans:bean class="com.example.web.security.CustomLdapAuthoritiesPopulator" />
    </beans:constructor-arg>
</beans:bean>

然后我尝试扩展 BindAuthenticator 以在构造函数中接受并设置辅助上下文源。最初我无法让它工作,所以我完全重写了 BindAuthenticator 类并扩展了 AbstractLdapAuthenticator 以绕过 BindAuthenticator。然后我覆盖了 authenticate 方法以检查用户名是否包含辅助 DN,如果包含,我将再次调用 bindWithDn 以尝试重新绑定到辅助域。这就是我认为我做错了的地方,因为当它尝试获取新的 Dn 时它失败了。基本上它声明它无法绑定到域。(我已经三次检查了域设置,并使用 ldap 管理控制台连接到它,并将这些设置用于我的应用程序)这是我的扩展 BindAuthenticator

public class ExtendedBindAuthenticator extends AbstractLdapAuthenticator {

    private BaseLdapPathContextSource secondaryContextSource;

    public void setSecondContextSource(BaseLdapPathContextSource secondContextSource) {
        this.secondaryContextSource = secondaryContextSource;
    }


    public ExtendedBindAuthenticator(BaseLdapPathContextSource contextSource, BaseLdapPathContextSource secondContextSource) {
        super(contextSource);
        this.secondaryContextSource = secondaryContextSource;
    }


    public DirContextOperations authenticate(Authentication authentication) {
        DirContextOperations user = null;
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                "Can only process UsernamePasswordAuthenticationToken objects");

        String username = authentication.getName();
        String password = (String)authentication.getCredentials();         

        if(username.contains("secondDomain")) {
            DirContextOperations secondaryUser = getUserSearch().searchForUser(username);
            this.bindWithDn(secondaryUser.getDn().toString(), username, password);

        }


        if (!StringUtils.hasLength(password)) {

            throw new BadCredentialsException(messages.getMessage("BindAuthenticator.emptyPassword",
                    "Empty Password"));
        }

        // If DN patterns are configured, try authenticating with them directly
        for (String dn : getUserDns(username)) {
            user = this.bindWithDn(dn, username, password);

            if (user != null) {
                break;
            }
        }

        // Otherwise use the configured search object to find the user and authenticate with the returned DN.
        if (user == null && getUserSearch() != null) {
            DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
            user = bindWithDn(userFromSearch.getDn().toString(), username, password);
        }

        if (user == null) {
            throw new BadCredentialsException(
                    messages.getMessage("BindAuthenticator.badCredentials", "Bad credentials"));
        }

        return user;
    }

    private DirContextOperations bindWithDn(String userDnStr, String username, String password) {
        BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();

        if(username.contains("secondDomain")) {
            ctxSource = secondaryContextSource;
        }

        DistinguishedName userDn = new DistinguishedName(userDnStr);
        DistinguishedName fullDn = new DistinguishedName(userDn);
        fullDn.prepend(ctxSource.getBaseLdapPath());


        DirContext ctx = null;
        try {
            ctx = getContextSource().getContext(fullDn.toString(), password);
            // Check for password policy control
            PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor.extractControl(ctx);



            Attributes attrs = ctx.getAttributes(userDn, getUserAttributes());

            DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath());

            if (ppolicy != null) {
                result.setAttributeValue(ppolicy.getID(), ppolicy);
            }

            return result;
        } catch (NamingException e) {
            // This will be thrown if an invalid user name is used and the method may
            // be called multiple times to try different names, so we trap the exception
            // unless a subclass wishes to implement more specialized behaviour.
            if ((e instanceof org.springframework.ldap.AuthenticationException)
                    || (e instanceof org.springframework.ldap.OperationNotSupportedException)) {
                handleBindException(userDnStr, username, e);
            } else {
                throw e;
            }
        } catch (javax.naming.NamingException e) {
            throw LdapUtils.convertLdapException(e);
        } finally {
            LdapUtils.closeContext(ctx);
        }

        return null;
    }

    /**
     * Allows subclasses to inspect the exception thrown by an attempt to bind with a particular DN.
     * The default implementation just reports the failure to the debug logger.
     */
    protected void handleBindException(String userDn, String username, Throwable cause) {
       System.out.println("Failed to bind as " + userDn + ": " + cause);
    }

}

如果有人对这种事情有任何经验,我将不胜感激,因为我在这个主题上找不到太多。我希望有人能告诉我我是否走在正确的轨道上,或者我是否应该以不同的方式解决这个问题。为了清楚起见,我使用的是 spring-security-ldap 而不是 spring-ldap。还只想提一下,我的 pom 文件中确实有我的所有依赖项。谢谢!

4

2 回答 2

3

从您的问题中并不能完全清楚到底出了什么问题 - 例如,您的配置实际上不会加载,因为它使用 Spring SecurityBindAuthenticator并尝试向ContextSource它传递两个参数。

如果我是你,我会避免尝试破解内部实现类,而是根据你的选择标准将它们单独放置并编写一个单独的委托类。

首先,我将定义两个单独LdapAuthenticationProvider的 bean,一个用于每个域,首先确保您可以通过在单元测试中直接调用它们来对每个用户进行身份验证。在尝试同时使用它们之前,请确保您可以为各自的域正确配置它们。

之后,我会将它们连接到一个单独的委派AuthenticationProvider中。就像是:

public class DelegatingLdapAuthenticationProvider implements AuthenticationProvider {
    // Inject these via the app context
    private LdapAuthenticationProvider primary;
    private LdapAuthenticationProvider secondary;

    public Authentication authenticate(Authentication a) {
        if (a.getName().contains("secondDomain")) {
            return secondary.authenticate(a);
        } else {
            return primary.authenticate(a);
        }
    }
}

然后我会将此 bean 配置为 Spring Security 实际调用的身份验证提供程序。

于 2013-11-25T15:54:09.203 回答
0

我已经使用了下面的实现,如果在一个 ldap/ad 中找不到凭证,它将检查另一个 ldap/ad 并做出相应的响应:

@Configuration
@EnableWebSecurity
@EnableConfigurationProperties
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            configureLdapP(auth);
            configureActiveDirectoryP(auth);
            configureLdap(auth);
            configureActiveDirectory(auth);
        }

    @Bean
    protected LdapLoginRequestFilter ldapLoginRequestFilter() throws Exception {
        return new LdapLoginRequestFilter("/login/ldap", authenticationManager(), authenticationResultHandler);
    }


    private void configureActiveDirectory(AuthenticationManagerBuilder auth) {
        ActiveDirectoryLdapAuthenticationProvider adProvider = activeDirectoryLdapAuthenticationProvider();
        if(adProvider != null) auth.authenticationProvider(adProvider);
    }

    private void configureActiveDirectoryP(AuthenticationManagerBuilder auth) {
        ActiveDirectoryLdapAuthenticationProvider adProvider = activeDirectoryLdapAuthenticationProviderP();
        if(adProvider != null) auth.authenticationProvider(adProvider);
    }

    private void configureLdap(AuthenticationManagerBuilder auth) throws Exception {
        String ldapServerUrl = "ldap url 1";
        String ldapUserDnPattern = "ldap user dn pattern";
        if (StringUtils.isNotBlank(ldapServerUrl) && StringUtils.isNotBlank(ldapUserDnPattern)) {
            auth.ldapAuthentication()
            .userDnPatterns(ldapUserDnPattern)
            .contextSource().url(ldapServerUrl);
        }
    }

    private void configureLdapP(AuthenticationManagerBuilder auth) throws Exception {
        String ldapServerUrl = "ldap url 2";
        String ldapUserDnPattern = "ldap user dn pattern";
        if (StringUtils.isNotBlank(ldapServerUrl) && StringUtils.isNotBlank(ldapUserDnPattern)) {
            auth.ldapAuthentication()
            .userDnPatterns(ldapUserDnPattern)
            .contextSource().url(ldapServerUrl);
        }
    }
    @Bean
    protected ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {


        ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain 2", "ad url 2",
                null);
        provider.setConvertSubErrorCodesToExceptions(true);
        provider.setUseAuthenticationRequestCredentials(true);
        provider.setUserDetailsContextMapper(new CustomUserDetailsContextMapper());
        return provider;
    }

    @Bean
    protected ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProviderP() {

        ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain1", "ad url 1",
                null);
        provider.setConvertSubErrorCodesToExceptions(true);
        provider.setUseAuthenticationRequestCredentials(true);
        provider.setUserDetailsContextMapper(new CustomUserDetailsContextMapper());
        return provider;
    }
}
于 2017-09-03T14:37:42.667 回答