Я пытаюсь добавить вторичный ldap contextSource для безопасности spring в среде с разделенным доменом, и, похоже, мне не хватает. Я понимаю, что подобные вопросы задавались и раньше, но это для двух отдельных доменов, регистрирующихся в одном и том же приложении.
Мой первый шаг состоял в том, чтобы добавить вторичный источник контекста в мой 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. Затем я переопределил метод аутентификации, чтобы проверить, содержит ли имя пользователя вторичное 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. Спасибо!