0

分割ドメイン環境でセキュリティを強化するためにセカンダリ ldap contextSource を追加しようとしていますが、不足しているようです。以前にも同様の質問があったことは承知していますが、これは同じアプリケーションにログインしている 2 つの別々のドメインに関するものです。

最初のステップは、セカンダリ コンテキスト ソースを次のように 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 Security を使用して2 つの引数をBindAuthenticator渡そうとしているため、構成が実際にはロードされません。ContextSource

もし私があなたなら、内部実装クラスをハックしようとするのを避け、代わりにそれらをそのままにして、選択基準に基づいて別の委譲クラスを作成します。

最初に、ドメインごとに 1 つずつ、2 つの別個のLdapAuthenticationProviderBean を定義し、最初に単体テストで直接呼び出して、それぞれのユーザーを認証できることを確認します。両方を一緒に使用する前に、それぞれのドメインに対してそれぞれを正しく構成できることを確認してください。

その後、それらを別の委任に配線します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 に答える