在多域环境中将多个ldap源添加到spring-security [英] Adding multiple ldap sources to spring-security in multi-domain environment

查看:81
本文介绍了在多域环境中将多个ldap源添加到spring-security的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试添加辅助ldap contextSource来在拆分域环境中提高弹簧安全性,但我似乎言之凿凿.我意识到之前曾问过类似的问题,但这是针对登录到同一应用程序的两个独立域的.

I am trying to add a secondary ldap contextSource to spring security in a split domain environment and I seem to be coming up short. I realize similar questions have been asked before but this is for two separate domains logging into the same application.

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

My first step was to add the secondary context source to my security-config xml file like so:

<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添加了Constructor-arg,并用我的自定义类替换了BindAuthenticator,如下所示:

In addition I added the constructor-arg to the ldapAuthenticationProvider and replaced the BindAuthenticator with my custom class like so:

    <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

Then I attempted to extend BindAuthenticator to accept and set a secondary context source in the constructor. Initially I couldn't get this to work so I completely rewrote the BindAuthenticator class and extended AbstractLdapAuthenticator to get around BindAuthenticator. Then I overrode the authenticate method to check if the username contained the secondary DN and if it did, I would call bindWithDn again to attempt to rebind to the secondary domain. This is where I think I am going about this all wrong because when it attempts to get the new Dn it fails. Basically it states that it could not bind to the domain. (I have triple checked the domain settings and have connected to it with a ldap administration console and took those settings for my app) Here is my extended 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文件中确实包含了所有依赖项.谢谢!

If anyone has any experience with this kind of thing I would greatly appreciate this as I couldn't find much on this subject. I was hoping that someone could tell me if I am on the right track or if I should be going about this in a different way. Just to be clear I am using spring-security-ldap and not spring-ldap. Also just want to mention that I do have all my dependencies in my pom file. Thanks!

推荐答案

从您的问题中尚不能完全清楚到底出了什么问题-例如,由于使用的是Spring Security的BindAuthenticator,因此您实际上并未加载该配置,并且试图将两个ContextSource参数传递给它.

It's not completely clear from your question what's actually going wrong - for example the configuration you have wouldn't actually load since it is using Spring Security's BindAuthenticator and attempting to pass two ContextSource arguments to it.

如果我是您,我将避免尝试破解内部实现类,而是根据您的选择标准,不理会内部实现类,并编写一个单独的委托类.

If I were you I would avoid trying to hack the internal implementation classes and instead leave them alone and write a separate delegation class, based on your selection criteria.

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

First I would define two separate LdapAuthenticationProvider beans, one for each domain and first make sure that you can authenticate users with each of them by calling them directly in a unit test. Make sure you can configure each of them correctly for their respective domains before you try and use both together.

之后,我将它们连接到一个单独的委托AuthenticationProvider中.像这样:

After that I would wire them into a separate delegating AuthenticationProvider. Something like:

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实际调用的身份验证提供程序.

I would then configure this bean as the authentication provider which Spring Security actually calls.

这篇关于在多域环境中将多个ldap源添加到spring-security的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆