分割ドメイン環境でセキュリティを強化するためにセカンダリ 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 ファイルにすべての依存関係があることにも言及したいと思います。ありがとう!