使用 Spring Security 3 进行 LDAP 身份验证

Posted

技术标签:

【中文标题】使用 Spring Security 3 进行 LDAP 身份验证【英文标题】:LDAP Authentication with Spring Security 3 【发布时间】:2012-11-18 21:10:23 【问题描述】:

我正在尝试通过对我组织的 LDAP 服务器进行身份验证来保护我的 Spring 3 MVC Web 应用程序的某些部分。我是 LDAP 新手,所以我边走边学。我一直在关注文档here 和示例here,但我似乎无法正确理解。

这是我的 security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:s="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- Security Configuration -->
    <s:http>
        <s:intercept-url pattern="/page/tosecure/*" access="ROLE_USER" />
        <s:http-basic />
    </s:http>

    <s:ldap-server root="dc=ldap,dc=sub,dc=myorg,dc=org" url="ldap.sub.myorg.org" port="636" />

    <s:authentication-manager>
        <s:ldap-authentication-provider user-dn-pattern="uid=0,cn=users" />
        <s:authentication-provider ref="ldapAuthProvider" />
    </s:authentication-manager>

    <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
        <constructor-arg value="ldaps://ldap.sub.myorg.org:636/dc=ldap,dc=sub,dc=myorg,dc=org" />
    </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,cn=users</value>
                    </list>
                </property>
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
                <constructor-arg ref="contextSource" />
                <constructor-arg value="cn=groups" />
                <property name="groupRoleAttribute" value="cn" />
            </bean>
        </constructor-arg>
    </bean>

</beans>

这是我得到的错误(堆栈跟踪中列出的最后几个原因)

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.securityContextSource': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.security.ldap.DefaultSpringSecurityContextSource]: Constructor threw exception; nested exception is org.springframework.ldap.BadLdapGrammarException: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 5.  Encountered: "." (46), after : ""
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:288)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:323)
    ... 106 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.security.ldap.DefaultSpringSecurityContextSource]: Constructor threw exception; nested exception is org.springframework.ldap.BadLdapGrammarException: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 5.  Encountered: "." (46), after : ""
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:121)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:280)
    ... 115 more
Caused by: org.springframework.ldap.BadLdapGrammarException: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 5.  Encountered: "." (46), after : ""
    at org.springframework.ldap.core.DistinguishedName.parse(DistinguishedName.java:224)
    at org.springframework.ldap.core.DistinguishedName.<init>(DistinguishedName.java:174)
    at org.springframework.ldap.core.support.AbstractContextSource.setBase(AbstractContextSource.java:207)
    at org.springframework.security.ldap.DefaultSpringSecurityContextSource.<init>(DefaultSpringSecurityContextSource.java:67)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
    ... 117 more
Caused by: org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 5.  Encountered: "." (46), after : ""
    at org.springframework.ldap.core.DnParserImplTokenManager.getNextToken(DnParserImplTokenManager.java:678)
    at org.springframework.ldap.core.DnParserImpl.jj_consume_token(DnParserImpl.java:231)
    at org.springframework.ldap.core.DnParserImpl.SpacedEquals(DnParserImpl.java:114)
    at org.springframework.ldap.core.DnParserImpl.attributeTypeAndValue(DnParserImpl.java:94)
    at org.springframework.ldap.core.DnParserImpl.rdn(DnParserImpl.java:58)
    at org.springframework.ldap.core.DnParserImpl.dn(DnParserImpl.java:23)
    at org.springframework.ldap.core.DistinguishedName.parse(DistinguishedName.java:218)

它似乎不喜欢 contextSource bean 的构造函数参数中列出的 URL,尽管我不确定为什么。

另外,我怀疑此配置的其他部分不正确。例如,我在 ldap-server 标记和 contextSource bean 中定义了 ldap 服务器 URL。这似乎是不必要的重复,但在示例中就是这样做的。有人可以仔细查看配置以确保其正常吗?

另外,如果有必要,我会稍微谈谈我们的 LDAP 服务器布局,因为它似乎有点不标准。用户的 DN 由 uid=the_user_name,cn=users,dc=ldap,dc=sub,dc=myorg,dc=org 构成。组 DN 是 cn=group_name,cn=groups,dc=ldap,dc=sub,dc=myorg,dc=org,组的成员由 memberUid 属性定义。我说这是非标准的,因为根据我的阅读,组应该由 ou 定义。但希望 spring security 可以处理这个设置。此配置是否正确获取用户所属的角色(组)?

【问题讨论】:

【参考方案1】:

您是否尝试过删除ldap-server 元素?您不应该需要它,并且您没有使用正确的 URL 配置它(它可能应该以 ldap://ldaps:// 开头)。

您链接到的示例使用嵌入式服务器,并说明了同一事物的命名空间和 bean 配置。

组属性默认为cn,因此对于您的设置应该是正确的。 DefaultLdapAuthoritiesPopulator 的 Javadoc 很好地描述了它的工作原理。

【讨论】:

在您的帮助下,我将配置简化为 &lt;s:http&gt; &lt;s:intercept-url pattern="/page/tosecure/*" access="IS_AUTHENTICATED_REMEMBERED" /&gt; &lt;s:http-basic /&gt; &lt;/s:http&gt; &lt;s:ldap-server url="ldaps://ldap.sub.myorg.org/dc=ldap,dc=sub,dc=myorg,dc=org" port="636"/&gt; &lt;s:authentication-manager&gt; &lt;s:ldap-authentication-provider user-dn-pattern="uid=0,cn=users" /&gt; &lt;/s:authentication-manager&gt; 但是,我希望能够限制对特定组的访问。在拦截 URL 中,我更改了对 ROLE_GROUPNAME 的访问权限并将 group-search-filter="memberUid=0" 添加到 s:ldap-authentication-provider 但我总是被拒绝访问。文档说 group-search-filter 将替换完整的 DN,而不仅仅是像我的 LDAP 服务器那样的用户名。如何配置它以匹配我的 LDAP 服务器? 啊,我想通了。如果组仅包含用户名而不是完整的 DN,我应该使用 group-search-filter="memberUid=1" 而不是 group-search-filter="memberUid=0"。

以上是关于使用 Spring Security 3 进行 LDAP 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

配置 ADFS 3.0 / SAML 2.0 以使用 Spring Security 进行 SSO 集成

使用 Spring Security 3.2.x 进行两页登录

Spring Security - 通过 URL 进行身份验证

使用 Spring Security 3.0.2 进行 OpenId 身份验证和自动注册

Spring security 3 在使用 OpenID 进行身份验证时忽略禁用/锁定标志

Spring-Security:升级到 Spring-Security 4.1 后,用户名发送为空以进行登录