LDAP根查询语法:搜索多个特定OU的方法

38

我需要运行一个单一的LDAP查询,以在根查询中搜索两个特定的组织单位(OU),但我遇到了困难。我尝试了下面的查询,但都不成功:

(|(OU=Staff,DC=my,DC=super,DC=org)(OU=Vendors,DC=my,DC=super,DC=org))

((OU=Staff,DC=my,DC=super,DC=org) | (OU=Vendors,DC=my,DC=super,DC=org))

我的问题是:是否可以在一个LDAP查询中查询多个单个OU?假设这是正确的语法,以便在根LDAP查询中使用此类型的表达式。


如果LDAP客户端搜索的条目包含组成对象类的objectClasses允许ou属性,则可以在搜索过滤器中使用ou属性。当然,这需要将ou属性添加到相关条目中。这可能是一个有效的解决方案,因为AD不支持可扩展匹配过滤器的出色建议。 - Terry Gardner
如果您能将我的答案标记为已接受的答案,那就太好了,因为当前被接受的答案似乎显然不完全有效(或者说不再有效?),并且在AD方面是错误的,因此一般而言也是错误的。它可能仅适用于某些LDAP实现。 - Andreas Covidiot
6个回答

30
你可以这样做!简单来说,使用以下内容作为连接字符串:
ldap://<host>:3268/DC=<my>,DC=<domain>?cn

与您的搜索过滤器一起,例如。

(&(sAMAccountName={0})(&((objectCategory=person)(objectclass=user)(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf:1.2.840.113556.1.4.1941:=CN=<some-special-nested-group>,OU=<ou3>,OU=<ou2>,OU=<ou1>,DC=<dc3>,DC=<dc2>,DC=<dc1>))))

那将在所谓的全局目录中搜索,在我们的环境中,这是开箱即用的。与多个OU一起使用时,而不是已知/常见的其他版本(或其组合)在我们的环境中无法正常工作
ldap://<host>/DC=<my>,DC=<domain>
ldap://<host>:389/DC=<my>,DC=<domain>  (standard port)
ldap://<host>/OU=<someOU>,DC=<my>,DC=<domain>
ldap://<host>/CN=<someCN>,DC=<my>,DC=<domain>
ldap://<host>/(|(OU=<someOU1>)(OU=<someOU2>)),DC=<my>,DC=<domain> (search filters here shouldn't work at all by definition)

(I am a developer, not an AD/LDAP guru:) 我是一名开发人员,而不是AD / LDAP专家 :)。该死,我已经在各处搜索这个解决方案将近2天,几乎放弃了,开始适应我可能必须手动实现这个显然非常常见的场景(使用Jasperserver / Spring security(/ Tomcat))的想法。 (因此,如果其他人或我将来再次遇到此问题,这将是一个提醒:O))

以下是我在研究过程中发现的其他相关线程,它们大多没有什么帮助:

以下是我们匿名化后的Tomcat LDAP配置,希望对您有所帮助(路径为/var/lib/tomcat7/webapps/jasperserver/WEB-INF/applicationContext-externalAUTH-LDAP.xml):

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<!-- ############ LDAP authentication ############ - Sample configuration 
    of external authentication via an external LDAP server. -->


<bean id="proxyAuthenticationProcessingFilter"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.BaseAuthenticationProcessingFilter">
    <property name="authenticationManager">
        <ref local="ldapAuthenticationManager" />
    </property>
    <property name="externalDataSynchronizer">
        <ref local="externalDataSynchronizer" />
    </property>

    <property name="sessionRegistry">
        <ref bean="sessionRegistry" />
    </property>

    <property name="internalAuthenticationFailureUrl" value="/login.html?error=1" />
    <property name="defaultTargetUrl" value="/loginsuccess.html" />
    <property name="invalidateSessionOnSuccessfulAuthentication"
        value="true" />
    <property name="migrateInvalidatedSessionAttributes" value="true" />
</bean>

<bean id="proxyAuthenticationSoapProcessingFilter"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.DefaultAuthenticationSoapProcessingFilter">
    <property name="authenticationManager" ref="ldapAuthenticationManager" />
    <property name="externalDataSynchronizer" ref="externalDataSynchronizer" />

    <property name="invalidateSessionOnSuccessfulAuthentication"
        value="true" />
    <property name="migrateInvalidatedSessionAttributes" value="true" />
    <property name="filterProcessesUrl" value="/services" />
</bean>

<bean id="proxyRequestParameterAuthenticationFilter"
    class="com.jaspersoft.jasperserver.war.util.ExternalRequestParameterAuthenticationFilter">
    <property name="authenticationManager">
        <ref local="ldapAuthenticationManager" />
    </property>
    <property name="externalDataSynchronizer" ref="externalDataSynchronizer" />

    <property name="authenticationFailureUrl">
        <value>/login.html?error=1</value>
    </property>
    <property name="excludeUrls">
        <list>
            <value>/j_spring_switch_user</value>
        </list>
    </property>
</bean>

<bean id="proxyBasicProcessingFilter"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalAuthBasicProcessingFilter">
    <property name="authenticationManager" ref="ldapAuthenticationManager" />
    <property name="externalDataSynchronizer" ref="externalDataSynchronizer" />

    <property name="authenticationEntryPoint">
        <ref local="basicProcessingFilterEntryPoint" />
    </property>
</bean>

<bean id="proxyAuthenticationRestProcessingFilter"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.DefaultAuthenticationRestProcessingFilter">
    <property name="authenticationManager">
        <ref local="ldapAuthenticationManager" />
    </property>
    <property name="externalDataSynchronizer">
        <ref local="externalDataSynchronizer" />
    </property>

    <property name="filterProcessesUrl" value="/rest/login" />
    <property name="invalidateSessionOnSuccessfulAuthentication"
        value="true" />
    <property name="migrateInvalidatedSessionAttributes" value="true" />
</bean>



<bean id="ldapAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
    <property name="providers">
        <list>
            <ref local="ldapAuthenticationProvider" />
            <ref bean="${bean.daoAuthenticationProvider}" />
            <!--anonymousAuthenticationProvider only needed if filterInvocationInterceptor.alwaysReauthenticate 
                is set to true <ref bean="anonymousAuthenticationProvider"/> -->
        </list>
    </property>
</bean>

<bean id="ldapAuthenticationProvider"
    class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
    <constructor-arg>
        <bean
            class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
            <constructor-arg>
                <ref local="ldapContextSource" />
            </constructor-arg>
            <property name="userSearch" ref="userSearch" />
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean
            class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
            <constructor-arg index="0">
                <ref local="ldapContextSource" />
            </constructor-arg>
            <constructor-arg index="1">
                <value></value>
            </constructor-arg>

            <property name="groupRoleAttribute" value="cn" />
            <property name="convertToUpperCase" value="true" />
            <property name="rolePrefix" value="ROLE_" />
            <property name="groupSearchFilter"
                value="(&amp;(member={0})(&amp;(objectCategory=Group)(objectclass=group)(cn=my-nested-group-name)))" />
            <property name="searchSubtree" value="true" />
            <!-- Can setup additional external default roles here <property name="defaultRole" 
                value="LDAP"/> -->
        </bean>
    </constructor-arg>
</bean>

<bean id="userSearch"
    class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
    <constructor-arg index="0">
        <value></value>
    </constructor-arg>
    <constructor-arg index="1">
        <value>(&amp;(sAMAccountName={0})(&amp;((objectCategory=person)(objectclass=user)(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf:1.2.840.113556.1.4.1941:=CN=my-nested-group-name,OU=ou3,OU=ou2,OU=ou1,DC=dc3,DC=dc2,DC=dc1))))
        </value>
    </constructor-arg>
    <constructor-arg index="2">
        <ref local="ldapContextSource" />
    </constructor-arg>
    <property name="searchSubtree">
        <value>true</value>
    </property>
</bean>

<bean id="ldapContextSource"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.ldap.JSLdapContextSource">
    <constructor-arg value="ldap://myhost:3268/DC=dc3,DC=dc2,DC=dc1?cn" />
    <!-- manager user name and password (may not be needed) -->
    <property name="userDn" value="CN=someuser,OU=ou4,OU=1,DC=dc3,DC=dc2,DC=dc1" />
    <property name="password" value="somepass" />
    <!--End Changes -->
</bean>
<!-- ############ LDAP authentication ############ -->

<!-- ############ JRS Synchronizer ############ -->
<bean id="externalDataSynchronizer"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalDataSynchronizerImpl">
    <property name="externalUserProcessors">
        <list>
            <ref local="externalUserSetupProcessor" />
            <!-- Example processor for creating user folder -->
            <!--<ref local="externalUserFolderProcessor"/> -->
        </list>
    </property>
</bean>

<bean id="abstractExternalProcessor"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.AbstractExternalUserProcessor"
    abstract="true">
    <property name="repositoryService" ref="${bean.repositoryService}" />
    <property name="userAuthorityService" ref="${bean.userAuthorityService}" />
    <property name="tenantService" ref="${bean.tenantService}" />
    <property name="profileAttributeService" ref="profileAttributeService" />
    <property name="objectPermissionService" ref="objectPermissionService" />
</bean>

<bean id="externalUserSetupProcessor"
    class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserSetupProcessor"
    parent="abstractExternalProcessor">
    <property name="userAuthorityService">
        <ref bean="${bean.internalUserAuthorityService}" />
    </property>
    <property name="defaultInternalRoles">
        <list>
            <value>ROLE_USER</value>
        </list>
    </property>

    <property name="organizationRoleMap">
        <map>
            <!-- Example of mapping customer roles to JRS roles -->
            <entry>
                <key>
                    <value>ROLE_MY-NESTED-GROUP-NAME</value>
                </key>
                <!-- JRS role that the <key> external role is mapped to -->
                <value>ROLE_USER</value>
            </entry>
        </map>
    </property>
</bean>

<!--bean id="externalUserFolderProcessor" class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserFolderProcessor" 
    parent="abstractExternalProcessor"> <property name="repositoryService" ref="${bean.unsecureRepositoryService}"/> 
    </bean -->

<!-- ############ JRS Synchronizer ############ -->


嗨,我有同样的问题,但我正在使用不同的代码。你能否请阅读我的问题:http://stackoverflow.com/questions/42971315/finding-active-directory-users-from-2-ou - user2931442

16
答案是否定的。为什么呢?
因为LDAP标准描述了一个带有4个参数的LDAP-SEARCH函数:
1. 搜索开始的节点,它是一个区分名称(DN)。 2. 要返回的属性。 3. 搜索的深度(基本、一级、子树)。 4. 过滤器。
你感兴趣的是过滤器。您可以在此处找到摘要here(由Microsoft提供给Active Directory的标准)。该过滤器以布尔方式组成,由类型为Attribute Operator Value的表达式组成。
因此,您提供的过滤器没有任何意义。
从理论上讲,有ExtensibleMatch可以在DN路径上构建过滤器,但是Active Directory不支持它。
据我所知,您必须在AD中使用属性来区分两个OU中的用户。
可以选择任何现有的鉴别属性,或者例如从organizationalPerson类继承的名为OU的属性。您可以为某些用户设置它(它不是自动的,并且如果移动用户,它将不会被维护),并为其他用户设置“vendors”,然后使用过滤器:
(&(objectCategory=person)(|(ou=staff)(ou=vendors)))

1
我们能否使用distinguishedName属性进行过滤,例如(distinguishedName=*OU路径)? - Santron Manibharathi
最佳的填充OU属性的方法是什么?我打算编写一个每天运行并查看用户所在OU,然后将唯一OU值写入OU属性的PS脚本。我假设OU只是一个名称,更改它不会影响其他任何事情? - DevilWAH
@DevilWAH,如果您查看模式,它是一个目录字符串,并且对于organizationalPerson或用户是可选的。 - JPBlanc
好的,太棒了。我确实在检查是否有可能“扔”一个开关来从DN中填充。既然可以,我会将其添加到每日维护脚本中。 - DevilWAH

7
很简单,只需更改端口。使用3268而不是389。如果您的域名为DOMAIN.LOCAL,请输入DC=DOMAIN,DC=LOCAL进行搜索。 端口3268:此端口用于针对全局目录进行的查询。发送到端口3268的LDAP请求可用于在整个森林中搜索对象。但是,仅可以返回标记为复制到全局目录的属性。 端口389:此端口用于从域控制器请求信息。发送到端口389的LDAP请求仅可用于在全局目录的主域中搜索对象。但是,应用程序可能获得所有搜索对象的属性。

1
这个端口是否支持 LDAPS,还是有另一个端口支持 LDAPS? - gary69
1
3268 是 GC 明文。3269 是默认加密的 GC SSL。389 是 AD 明文。636 是默认加密的 AD SSL。 - frisbee23

2

简而言之 -- 使用ou:dn:=<val>语法

对于提问者的问题,解决方法如下:

ldapsearch -b "DC=my,DC=super,DC=org" <other_options> "(|(ou:dc:=Staff)(ou:dc:=Vendors))"

原则上,您的情况比较棘手,因为OU值不会出现在用户的ldapsearch输出中-- OU值是DN的一部分。

例如,在这里进行的搜索结果:

[root@pg data]# ldapsearch -H "ldap://ldap-service" -D "cn=admin,dc=example,dc=org" -w admin -b"DC=example,DC=org" cn=testuser2 
# extended LDIF
#
# LDAPv3
# base <DC=example,DC=org> with scope subtree
# filter: cn=testuser2
# requesting: ALL
#

# testuser2, AU, IIQ, example.org
dn: cn=testuser2,ou=AU,ou=IIQ,dc=example,dc=org
cn: testuser2
displayName: pgtest
gidNumber: 500
givenName: testuser2
homeDirectory: /home/testuser2
loginShell: /bin/sh
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
userPassword:: e01ENX1GMnFxVVpDTjh4VVJveGg5bkJBcGF3PT0=
sn: testuser2
uidNumber: 1012
uid: testuser2

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

没有ou:行,因为OU属于dn:行。因此,如果您在搜索中添加一个ou,您会看到它是requesting: ou而不是requesting: ALL,但不会列出任何属性:

[root@pg data]# ldapsearch -H "ldap://ldap-service" -D "cn=admin,dc=example,dc=org" -w admin -b"DC=example,DC=org" cn=testuser2 ou
# extended LDIF
#
# LDAPv3
# base <DC=example,DC=org> with scope subtree
# filter: cn=testuser2
# requesting: ou 
#

# testuser2, AU, IIQ, example.org
dn: cn=testuser2,ou=AU,ou=IIQ,dc=example,dc=org

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

在OU上搜索会给你结果,但它们只是OU对象,而不是用户本身:

[root@pg data]# ldapsearch -H "ldap://ldap-service" -D "cn=admin,dc=example,dc=org" -w admin -b"DC=example,DC=org" "(|(OU=IN)(OU=AU))"
# extended LDIF
#
# LDAPv3
# base <DC=example,DC=org> with scope subtree
# filter: (|(OU=IN)(OU=AU))
# requesting: ALL
#

# IN, example.org
dn: ou=IN,dc=example,dc=org
ou: IN
objectClass: organizationalUnit
objectClass: top

# AU, example.org
dn: ou=AU,dc=example,dc=org
ou: AU
objectClass: organizationalUnit
objectClass: top

# IN, IIQ, example.org
dn: ou=IN,ou=IIQ,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: IN

# AU, IIQ, example.org
dn: ou=AU,ou=IIQ,dc=example,dc=org
ou: AU
objectClass: organizationalUnit
objectClass: top

# search result
search: 2
result: 0 Success

# numResponses: 5
# numEntries: 4

然而,ldapsearch 允许您使用 ou:dn:=<val> 语法提取 DN 的部分内容:

[root@pg data]# ldapsearch -H "ldap://ldap-service" -D "cn=admin,dc=example,dc=org" -w admin -b"DC=example,DC=org" "(&(displayname=pgtest)(|(ou:dn:=IN)(ou:dn:=AU)))" cn displayname
# extended LDIF
#
# LDAPv3
# base <DC=example,DC=org> with scope subtree
# filter: (&(displayname=pgtest)(|(ou:dn:=IN)(ou:dn:=AU)))
# requesting: cn displayname 
#

# testuser3, AU, example.org
dn: cn=testuser3,ou=AU,dc=example,dc=org
cn: testuser3
displayName: pgtest

# testuser4, IN, example.org
dn: cn=testuser4,ou=IN,dc=example,dc=org
cn: testuser4
displayName: pgtest

# testuser2, AU, IIQ, example.org
dn: cn=testuser2,ou=AU,ou=IIQ,dc=example,dc=org
cn: testuser2
displayName: pgtest

# testuser1, IN, IIQ, example.org
dn: cn=testuser1,ou=IN,ou=IIQ,dc=example,dc=org
cn: testuser1
displayName: pgtest

# testuser14, IN, example.org
dn: cn=testuser14,ou=IN,dc=example,dc=org
cn: testuser14
displayName: pgtest

# search result
search: 2
result: 0 Success

# numResponses: 6
# numEntries: 5

2
我认为在AD中不可能实现这一点。distinguishedName属性是我所知道的唯一包含你要搜索的OU部分的内容,因此你需要使用通配符来获取那些OU下的对象结果。不幸的是,通配符字符在DNs上不受支持
如果有可能的话,我建议你使用OU=Staff...和OU=Vendors...作为基本DN进行两次查询。

不幸的是,SharePoint将其用作根查询来解析成员,我无法更改SharePoint。 - James
我们曾经遇到类似的情况。我们通过其他属性的组合解决了这个问题:邮件、用户账户控制等,只识别出我们需要的用户。 - vinny
1
在SharePoint的情况下,为什么不添加另一个导入连接,以OU作为搜索基础?以下是多个森林的操作方法:http://technet.microsoft.com/en-us/library/cc263247(office.12).aspx - vinny
这不仅仅是用于配置文件导入吗?我的需求更多涉及SharePoint中的名称解析(例如People Picker控件)。 - James
没错 - 我没有意识到您想要限制人员选择器。我建议您编辑原始问题,注明您要过滤的内容,否则可能会看到另一个“无法完成”的答案。在人员选择器的情况下,使用Peoplepicker-serviceaccountdirectorypaths stsadm属性似乎是可能的:http://technet.microsoft.com/en-us/library/cc263012(office.12).aspx。您只需要提供一个以分号分隔的OU列表作为属性值。所以,在您的情况下,“OU=Staff,DC=my,DC=super,DC=org;OU=Vendors,DC=my,DC=super,DC=org”。 - vinny

2

与LDAP专家交谈后,发现这种方式不可行。一个查询无法搜索多个DC或OU。

您有以下选项:

  1. 运行多个查询并解析结果。
  2. 使用过滤器根据不同的属性(如AD组或名称)查找所需的用户/对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接