具有多用户 OU 和多访问 CN 的 Spring LDAP 身份验证

Posted

技术标签:

【中文标题】具有多用户 OU 和多访问 CN 的 Spring LDAP 身份验证【英文标题】:Spring LDAP authentication with multiple user OU and multiple access CNs 【发布时间】:2014-08-13 09:43:46 【问题描述】:

如何尽可能使用 Spring Security/LDAP 解决以下 LDAP 认证情况。

用户属于 2 个 LDAP 组织单位之一 (ou):客户或员工

用户属于 3 个访问组之一 (cn - groupofuniquenames) 或其子组 (cn)

所以基本上是这样的:

    在 LDAP 中查找用户的 DN(客户或员工)

    绑定用户检查密码

    逐一搜索所有 3 个访问组及其子组,以查找具有用户 DN 的 uniquename 属性。

我查看了各种教程和示例,但似乎没有一个相关,我无法将它们组合起来。如果访问组是一个组织单位会更容易,但事实并非如此。

整个页面及其所有 servlet 都应该在身份验证之后。

问题有点具体,但希望对社区有用。欢迎任何想法或建议。

我目前使用的代码是spring文档的修改版本。

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
    <property name="rolePrefix" value=""></property>
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <constructor-arg name="decisionVoters" ref="roleVoter" />
</bean>

<security:http authentication-manager-ref="ldap-auth" access-decision-manager-ref="accessDecisionManager">
    <security:intercept-url pattern="/site/**" access="LDAP-Access-Group" />
    <security:form-login 
        login-page="/login" 
        authentication-failure-url="/denied"
        username-parameter="username" 
        password-parameter="password"
        default-target-url="/site/main" />
    <security:logout 
        invalidate-session="true" 
        logout-success-url="/login" 
        logout-url="/j_spring_security_logout" />
    <security:access-denied-handler error-page="/denied" />
    <security:session-management invalid-session-url="/login">
        <security:concurrency-control max-sessions="1" expired-url="/login" />            
    </security:session-management>
</security:http>

<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://server:389/o=company,c=com"/>
</bean>

<security:authentication-manager id="ldap-auth">            
    <security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
    <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
        <constructor-arg ref="contextSource"/>
        <property name="userDnPatterns">
            <list>
                <value>uid=0,ou=Employees</value>
                <value>uid=0,ou=Clients</value>
            </list>
        </property>
    </bean>
</constructor-arg>
<constructor-arg>
    <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <constructor-arg ref="contextSource"/>
        <constructor-arg value="ou=Access"/>
        <property name="searchSubtree" value="true"/>
        <property name="groupRoleAttribute" value="cn" />
    </bean>
</constructor-arg>
</bean>

上面的代码似乎没有返回权限。有没有办法将输出发送到调试控制台?无法读取 LDAP 日志。

另外,如果我注释掉 AuthoritiesPopulator,那么在检查安全标签(即&lt;sec:authorize access="isAuthenticated()"&gt;logged in&lt;/sec:authorize&gt;)时,身份验证似乎有效,但由于某种原因,intercept-url 阻止我使用&lt;security:intercept-url pattern="/site/**" access="isAuthenticated()" /&gt; 进入站点。没看懂。

【问题讨论】:

我认为您的意思是用户位于两个 LDAP 容器 之一(ou 通常是一个容器)。另外,您的 LDAP 服务器是 Active Directory 吗? @mvreijn 这不是广告。 OK,排除msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx 【参考方案1】:

您的第 1 步和第 2 步是 LDAP 身份验证的实际默认设置。

关于第 3 步:一些 LDAP 服务器维护组及其成员的互惠关系。因此,当将用户添加到组时,组上的member 属性和用户上的memberOf 属性都会被填充。这将简化事情,因为您可以检索用户的memberOf 属性并找到您的组。

由于您正在查询组,我认为您不是这种情况。我的建议是,创建一个包含 3 个主要访问组及其子组的所需组列表。根据所需的灵活性,您可能应该在那里应用一些缓存。

接下来,使用(&amp;(objectClass=groupOfUniqueNames)(member=cn=youruser,ou=some,o=org)) 查询 LDAP 服务器以查找所有将您的用户作为成员的组,仅返回 DN 列表,不返回属性(出于性能原因)。

现在您可以浏览返回的组列表,看看您想要的组是否在其中。

这种方法可确保您在登录时只需要一个查询总数(假设您缓存了组列表),而不是每个组一个。

【讨论】:

使用 memberOf 属性太容易了,我这里没有。所以基本上我必须编写我的自定义身份验证管理器,并且没有神奇的 Spring Security 配置可以在这种情况下工作? 我认为在使用 Spring 时不必编写所有这些代码。如果您配置group-search-base 并设置role-prefix="none",您将获得用户所属的组列表。然而 Spring 假定这些是 授权authciation 之后要检查的。您需要使用LdapAuthenticationProvider.loadUserAuthorities() 来检查您的群组的授权权限。【参考方案2】:

看来主要问题是在DefaultLdapAuthoritiesPopulator 中指定搜索库参数。在 DefaultLdapAuthoritiesPopulator bean 中将值更改为“”解决了问题并开始返回用户的权限。

<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://server:389/o=company,c=com"/>
    <property name="anonymousReadOnly" value="true"/>
</bean>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
    <constructor-arg>
        <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <constructor-arg ref="contextSource"/>
            <property name="userDnPatterns">
                <list>
                    <value>uid=0,ou=Employees</value>
                    <value>uid=0,ou=Clients</value>
                </list>
            </property>         
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
            <constructor-arg ref="contextSource"/>
            <constructor-arg value=""/>
            <property name="searchSubtree" value="true"/>
            <property name="groupRoleAttribute" value="cn"/>
            <property name="groupSearchFilter" value="uniquemember=0"/>   
        </bean>
    </constructor-arg>
</bean>

【讨论】:

以上是关于具有多用户 OU 和多访问 CN 的 Spring LDAP 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

EBS MOAC 多OU使用配置

具有一对多和多对多关系的 JOOQ pojos

Spring Batch - 分块和多线程步骤 - RowMapper 中的 Nullpointer 异常

Spring配置动态数据源-读写分离和多数据源

Hibernate、Spring 框架和多对多

Spring Security - 在Spring Boot中针对LDAP使用Active Directory对用户进行身份验证