如何配置 Spring Security 身份验证来处理复杂的 Active Directory / LDAP 帐户树?
Posted
技术标签:
【中文标题】如何配置 Spring Security 身份验证来处理复杂的 Active Directory / LDAP 帐户树?【英文标题】:How do I configure Spring Security authentication to deal with a complex Active Directory / LDAP account tree? 【发布时间】:2012-03-21 06:17:56 【问题描述】:(背景:我是一位经验丰富的程序员,但对 LDAP、AD 和 Spring 不熟悉。)
我们是一家 Windows 商店,因此我们所有的身份验证都是通过 Active Directory 完成的。我们正在尝试集成一个用 Java 编写的第三方产品,因此它使用 Spring Security 进行所有身份验证。到目前为止,一切都很好——他们之前已经完成了这种集成,并且网上有很多关于如何设置的文章。
问题是,我们的 AD 设置有点复杂:特别是,我们的用户帐户存在于 AD/LDAP 树的各个节点中。举一个简化的例子,假设 LDAP 树如下所示:
DC=my-domain,DC=com
+ CN=Users
++ CN=user1,CN=Users,DC=my-domain,DC=com
+ CN=Staff
++ CN=user2,CN=Staff,DC=my-domain,DC=com
问题是,我发现的所有示例都允许我验证 任一 user1 或 user2,但不能同时验证两者。也就是说,以下 XML sn-p 将根据“组”下定义的角色对 user1 进行身份验证:
<security:ldap-server url="ldap://my-domain.com:389" manager-dn="CN=manager_svc,OU=System Users,DC=my-domain,DC=com" manager-password="MyPa55w0rd"/>
<security:ldap-authentication-provider
user-dn-pattern=""
user-search-base="CN=Users,DC=my-domain,DC=com"
user-search-filter="(&(sAMAccountName=0)(objectclass=user))"
group-search-base="OU=Groups,DC=mydomain,DC=com"
group-search-filter="member=0"
/>
但这不会验证 user2,因为他不匹配用户搜索库。相反,我可以将 user-search-base 更改为 CN=Staff,DC=my-domain,DC=com
,这对 user2 有效,但对 user1 无效。
所以问题是,如何使这种搜索对分散在 AD/LDAP 树中的用户帐户起作用?我可以想象两种可能性,但我还没有想出怎么做:
1234563到目前为止,我所做的所有尝试都遇到了一个或另一个错误,但我仍在尝试。OTOH,存在搜索的子树范围。我可以在交互式 LDAP 工具中看到搜索可以是单级或子树。据我所知,开箱即用的 Spring 正在做单级。我可以看到底层的 FilterBasedLdapUserSearch 类有一个 setSearchSubtree() 方法,这看起来像我想要的,但我找不到从 XML 将其设置为 true 的方法。 (现在,我们假设更改底层 Java 程序是不可行的。)
第一个选项是理想的,因为它可能更有效,但如果这不可能,而第二个是,我怀疑我们可以让它工作。
我怀疑第二种方法可以使用棘手的 bean hackery,但我对 bean 几乎一无所知,所以我宁愿自己不要涉足那些灌木丛。有人有好的食谱可以推荐吗?
非常感谢您提供的任何指导...
【问题讨论】:
【参考方案1】:如果可行,您可以尝试从域根目录进行搜索,但这可能会导致 AD 出现问题。
或者,使用显式bean configuration 可能是您的最佳选择。您可以将自定义 LdapUserSearch
实现注入到 BindAuthenticator
bean 中,它会在所有必要的位置下进行搜索。如果您查看文档中的示例,它会显示 FilterBasedLdapUserSearch
配置。您可以使用其中的几个,也可以自己从头开始实现接口。以下是一个快速破解示例:
public class CustomLdapSearch implements LdapUserSearch
public static final String SAM_FILTER="(&(sAMAccountName=0)(objectclass=user))"
final LdapUserSearch users;
final LdapUserSearch staff;
public CustomLdapSearch(BaseLdapPathContextSource contextSource)
users = new FilterBasedLdapUserSearch("CN=Users,DC=my-domain,DC=com", SAM_FILTER, contextSource);
staff = new FilterBasedLdapUserSearch("CN=Staff,DC=my-domain,DC=com", SAM_FILTER, contextSource);
public DirContextOperations searchForUser(String username)
try
return users.searchForUser(username);
catch(UsernameNotFoundException e)
return staff.searchForUser(username);
然后将BindAuthenticator
配置改为:
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userSearch" ref="customSearch"/>
</bean>
<bean id="customSearch" class="CustomLdapSearch">
<constructor-arg ref="contextSource"/>
</bean>
【讨论】:
这个答案很有帮助,但它有问题。例如:您将“users”和“staff”声明为 LdapSearch,而它们应该是 LdapUserSearch。您还宣布它们是最终的,但没有将它们设置为任何值。 谢谢,我已经更新了代码。字段在构造函数中设置。【参考方案2】:我使用 FilterBasedLdapUserSearch 对 spring-security-2.0.x 做了类似的事情——用户分布在多个节点上:
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value=""/> <!-- optional sub-tree here -->
<constructor-arg value="(&(sAMAccountName=0)(objectclass=user))"/>
<constructor-arg ref="contextSource"/>
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userSearch" ref="ldapUserSearch"/>
</bean>
</constructor-arg>
<property name="userDetailsContextMapper" ref="userDetailsContextMapper"/>
</bean>
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://localhost:10389/CN=Users,DC=my-domain,DC=com"/>
<!-- you may or may not need to connect with an account that can search -->
<!--<property name="userDn" value="uid=admin,ou=system"/>-->
<!--<property name="password" value="secret"/>-->
</bean>
【讨论】:
【参考方案3】:我通过使用带有空字符串值的 searchBase 解决了这个问题(这使用根作为搜索库,就像 prule 的答案一样),但我还必须将属性“referral”设置为“follow”,否则我得到一个部分结果异常!
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(...);
contextSource.setReferral("follow");
【讨论】:
以上是关于如何配置 Spring Security 身份验证来处理复杂的 Active Directory / LDAP 帐户树?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 和 XML 配置在 Spring Security 中配置 jdbc 身份验证管理器?
如何使用 Spring Security 模拟身份验证和授权
如何配置 Spring Security 身份验证来处理复杂的 Active Directory / LDAP 帐户树?
如何使用 Hibernate 3 Annotated Classes 配置 Spring Security 2 数据库身份验证?