Spring LDAP参考(二)

6. 高级 LDAP 查询

本节介绍了如何将LDAP查询与Spring LDAP一起使用的各种方法。

6.1. LDAP 查询构建器参数

及其关联的类旨在支持可提供给 LDAP 搜索的所有参数。 支持以下参数:??LdapQueryBuilder??

??base??:指定 LDAP 树中应从搜索开始的根 DN。??searchScope??:指定搜索应遍历 LDAP 树的深度。??attributes??:指定要从搜索返回的属性。默认值为全部。??countLimit??:指定要从搜索返回的最大条目数。??timeLimit??:指定搜索可能需要的最长时间。搜索过滤器:我们要查找的条目必须满足的条件。

通过调用 的方法创建 。它旨在作为一个流畅的构建器 API,其中首先定义基本参数,然后是过滤器规范调用。一旦开始定义过滤条件,调用 的方法 ,以后的调用尝试(例如)将被拒绝。基本搜索参数是可选的,但至少需要一个筛选器规范调用。 以下查询搜索对象类为 :??LdapQueryBuilder????query????LdapQueryBuilder????where????LdapQueryBuilder????base????Person??

例 20。搜索具有对象类的所有条目??Person??

import static org.springframework.ldap.query.LdapQueryBuilder.query;…List<Person> persons = ldapTemplate.search( query().where(“objectclass”).is(“person”), new PersonAttributesMapper());

以下查询搜索对象类为 和 (公用名) 为 的所有条目:??person????cn????John Doe??

例 21。搜索具有对象类和??person????cn=John Doe??

import static org.springframework.ldap.query.LdapQueryBuilder.query;…List<Person> persons = ldapTemplate.search( query().where(“objectclass”).is(“person”) .and(“cn”).is(“John Doe”), new PersonAttributesMapper());

以下查询搜索对象类为 (域组件) 的所有条目:??person????dc????dc=261consulting,dc=com??

例 22。搜索对象类以 开头的所有条目??person????dc=261consulting,dc=com??

import static org.springframework.ldap.query.LdapQueryBuilder.query;…List<Person> persons = ldapTemplate.search( query().base(“dc=261consulting,dc=com”) .where(“objectclass”).is(“person”), new PersonAttributesMapper());

以下查询返回对象类为 (域组件) 的所有条目的 (公用名) 属性:??cn????person????dc????dc=261consulting,dc=com??

例 23。搜索类以 开头为 的所有条目,仅返回属性??Person????dc=261consulting,dc=com????cn??

import static org.springframework.ldap.query.LdapQueryBuilder.query;…List<Person> persons = ldapTemplate.search( query().base(“dc=261consulting,dc=com”) .attributes(“cn”) .where(“objectclass”).is(“person”), new PersonAttributesMapper());

以下查询用于搜索公用名 () 的多个拼写:??or????cn??

例 24。使用条件搜索??or??

import static org.springframework.ldap.query.LdapQueryBuilder.query;…List<Person> persons = ldapTemplate.search( query().where(“objectclass”).is(“person”), .and(query().where(“cn”).is(“Doe”).or(“cn”).is(“Doo”)); new PersonAttributesMapper());6.2. 筛选条件

前面的示例演示了 LDAP 筛选器中的简单相等条件。LDAP 查询构建器支持以下条件类型:

??is??:指定等于 (=) 条件。??gte??:指定大于或等于 (>=) 条件。??lte??:指定小于或等于 (?) 条件。??like??:指定可以在查询中包含通配符的“like”条件,例如,生成以下筛选器:。where(“cn”).like(“J*hn Doe”)(cn=J*hn Doe)??whitespaceWildcardsLike??:指定将所有空格替换为通配符的条件,例如,生成以下筛选器:。where(“cn”).whitespaceWildcardsLike(“John Doe”)(cn=John*Doe)??isPresent??:指定检查属性是否存在的条件,例如,生成以下筛选器:。where(“cn”).isPresent()(cn=*)??not??:指定应否定当前条件 — 例如,生成以下筛选器:where(“sn”).not().is(“Doe)(!(sn=Doe))6.3. 硬编码过滤器

有时,您可能希望将硬编码筛选器指定为 . 为此有两种方法:??LdapQuery????LdapQueryBuilder??

??filter(String hardcodedFilter)??:使用指定的字符串作为筛选器。请注意,不会以任何方式触及指定的输入字符串,这意味着如果要从用户输入构建筛选器,则此方法不是特别适合。??filter(String filterFormat, String… params)??:使用指定的字符串作为 的输入,正确编码参数并将其插入筛选器字符串中的指定位置。MessageFormat??filter(Filter filter)??:使用指定的过滤器。

不能将硬编码的筛选器方法与前面所述的方法混合使用。它要么是一个,要么是另一个。如果使用 指定筛选器,则在之后尝试调用时会出现异常。??where????filter()????where??

7. 配置

配置 Spring LDAP 的推荐方法是使用自定义 XML 配置命名空间。要使其可用,您需要在 Bean 文件中包含 Spring LDAP 命名空间声明,如下所示:

<beans xmlns=”http://www.springframework.org/schema/beans” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:ldap=”http://www.springframework.org/schema/ldap” xsi:schemaLocation=”http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap https://www.springframework.org/schema/ldap/spring-ldap.xsd”>7.1. 配置??ContextSource??

??ContextSource??通过使用标记定义。 最简单的声明要求您指定服务器 URL、用户名和密码,如下所示:??<ldap:context-source>????context-source??

例 25。最简单的上下文源声明

<ldap:context-source username=”cn=Administrator” password=”secret” url=”ldap://localhost:389″ />

前面的示例使用 默认值(请参阅本段后面的表)以及指定的 URL 和身份验证凭据创建一个。 上下文源上的可配置属性如下(标有 * 的必需属性):??LdapContextSource??

表 1.上下文源配置属性

属性

违约

描述

??id??

??contextSource??

创建的 Bean 的 ID。

??username??

向 LDAP 服务器进行身份验证时要使用的用户名(主体)。 这通常是管理员用户的可分辨名称(例如,),但可能因服务器和身份验证方法而异。 如果未显式配置,则为必需。??cn=Administrator????authentication-source-ref??

??password??

向 LDAP 服务器进行身份验证时要使用的密码(凭据)。如果未显式配置,则为必需。??authentication-source-ref??

??url??*

要使用的 LDAP 服务器的 URL。URL 应采用以下格式:。 对于 SSL 访问,请使用协议和相应的端口,例如 。 如果需要故障转移功能,可以指定多个 URL,用逗号 () 分隔。??ldap://myserver.example.com:389????ldaps????ldaps://myserver.example.com:636????,??

??base??

??LdapUtils.emptyLdapName()??

基本 DN。配置此属性后,提供给 LDAP 操作和从 LDAP 操作接收的所有可分辨名称都相对于指定的 LDAP 路径。 这可以大大简化针对 LDAP 树的工作。但是,在某些情况下,您需要访问基本路径。 有关此内容的详细信息,请参阅??获取对基本 LDAP 路径的引用??

??anonymous-read-only??

??false??

定义是否使用匿名(未经身份验证)上下文执行只读操作。 请注意,不支持将此参数与补偿事务支持一起设置,并且会被拒绝。??true??

??referral??

??null??

定义用于处理引荐的策略,如此??处??所述。有效值为:

??ignore????follow????throw??

??native-pooling??

??false??

指定是否应使用本机 Java LDAP 连接池。请考虑改用 Spring LDAP 连接池。有关详细信息??,请参阅池化支持??。

??authentication-source-ref??

一个实例。??SimpleAuthenticationSource??

要使用的实例的 ID(请参阅??自定义主体和凭证管理???)。??AuthenticationSource??

??authentication-strategy-ref??

一个实例。??SimpleDirContextAuthenticationStrategy??

要使用的实例的 ID(请参阅??自定义目录上下文身份验证处理???)。??DirContextAuthenticationStrategy??

??base-env-props-ref??

对自定义环境属性的引用,这些属性应随发送到构造的环境一起提供。??Map????DirContext??

7.1.1. 身份验证??DirContext??

创建用于在 LDAP 服务器上执行操作的实例时,通常需要对这些上下文进行身份验证。 Spring LDAP 提供了各种选项来配置它。??DirContext??

本节介绍 的核心功能中的身份验证上下文,以构造供 使用的实例。LDAP 通常用于用户身份验证的唯一目的,也可以用于此目的。该过程在使用 Spring LDAP 的用户身份验证?中进行了讨论。??ContextSource????DirContext????LdapTemplate????ContextSource??

默认情况下,将为只读和读写操作创建经过身份验证的上下文。您应该指定要用于对元素进行身份验证的 LDAP 用户的 and。??username????password????context-source??

如果是 LDAP 用户的可分辨名称 (DN),则无论是否已在元素上指定 LDAP 路径,它都需要是 LDAP 树根目录中用户的完整 DN。??username????base????context-source??

某些 LDAP 服务器设置允许匿名只读访问。如果要将匿名上下文用于只读操作,请将该属性设置为 。??anonymous-read-only????true??

自定义身份验证处理??DirContext??

Spring LDAP中使用的默认身份验证机制是身份验证。这意味着主体(由属性指定)和凭据(由 指定)在发送到实现构造函数的 中设置。??SIMPLE????username????password????Hashtable????DirContext??

在许多情况下,这种处理是不够的。例如,LDAP 服务器通常设置为仅接受安全 TLS 通道上的通信。可能需要使用特定的 LDAP 代理身份验证机制或其他问题。

可以通过提供对元素的实现引用来指定备用身份验证机制。为此,请设置属性。??DirContextAuthenticationStrategy????context-source????authentication-strategy-ref??

红绿灯

Spring LDAP 为需要 TLS 安全通道通信的 LDAP 服务器提供了两种不同的配置选项:和 。 这两种实现都在目标连接上协商 TLS 通道,但它们在实际身份验证机制上有所不同。 在安全通道上应用 SIMPLE 身份验证(通过使用指定的 和 ),则使用外部 SASL 身份验证,应用通过使用系统属性进行身份验证配置的客户端证书。??DefaultTlsDirContextAuthenticationStrategy????ExternalTlsDirContextAuthenticationStrategy????DefaultTlsDirContextAuthenticationStrategy????username????password????ExternalTlsDirContextAuthenticationStrategy??

由于不同的 LDAP 服务器实现对 TLS 通道显式关闭的响应不同(某些服务器要求正常关闭连接,而其他服务器不支持连接),因此 TLS 实现支持使用该参数指定关闭行为。如果此属性设置为 (默认值),则不会发生显式 TLS 关闭。如果是,Spring LDAP 会尝试在关闭目标上下文之前正常关闭 TLS 通道。??DirContextAuthenticationStrategy????shutdownTlsGracefully????false????true??

使用 TLS 连接时,需要确保关闭本机 LDAP 池功能(通过使用属性指定)。如果设置为 ,则这一点尤其重要。但是,由于 TLS 通道协商过程非常昂贵,因此您可以使用池化支持中所述的 Spring LDAP 池支持?获得巨大的性能优势。??native-pooling????shutdownTlsGracefully????false??

自定义主体和凭据管理

虽然用于创建经过身份验证的用户名(即用户 DN)和密码在缺省情况下是静态定义的(元素配置中定义的用户名和密码在 的整个生存期内使用),但在几种情况下,这不是所需的行为。一种常见情况是,在为当前用户执行 LDAP 操作时,应使用该用户的主体和凭据。可以通过通过使用元素提供对元素实现的引用来修改默认行为,而不是显式指定 和 。每次创建经过身份验证的主体和凭据时,都会查询 。??Context????context-source????ContextSource????AuthenticationSource????context-source????authentication-source-ref????username????password????AuthenticationSource????ContextSource????Context??

如果使用 Spring 安全性,则可以通过使用Spring 安全性附带的实例来确保始终使用当前登录用户的主体和凭据。以下示例演示如何执行此操作:??ContextSource????SpringSecurityAuthenticationSource??

例 26。使用 SpringSecurityAuthenticationSource

<beans>… <ldap:context-source url=”ldap://localhost:389″ authentication-source-ref=”springSecurityAuthenticationSource”/> <bean id=”springSecurityAuthenticationSource” class=”org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource” />…</beans>

我们不指定任何或为。我们在使用 .仅当使用默认行为时,才需要这些属性。??username????password????context-source????AuthenticationSource??

使用 时,您需要使用 Spring 安全性来针对 LDAP 对用户进行身份验证。??SpringSecurityAuthenticationSource????LdapAuthenticationProvider??

7.1.2. 原生 Java LDAP 池

内部 Java LDAP 提供程序提供了一些非常基本的池化功能。您可以使用 打开的标志打开或关闭此 LDAP 连接池。缺省值为(从 1.3 版开始)— 也就是说,本机 Java LDAP 池已关闭。LDAP 连接池的配置是使用属性管理的,因此您需要在 Spring 上下文配置之外手动处理此问题。您可以在此处找到本机池配置的详细信息。??pooled????AbstractContextSource????false????System??

内置的LDAP连接池中存在几个严重的缺陷,这就是为什么Spring LDAP提供了一种更复杂的LDAP连接池方法,如??池化支持??中所述。如果需要池化功能,这是推荐的方法。

无论池配置如何,该方法始终显式不使用本机 Java LDAP 池,以便重置密码尽快生效。??ContextSource#getContext(String principal, String credentials)??

7.1.3. 高级配置??ContextSource??

本节介绍配置 .??ContextSource??

自定义环境属性??DirContext??

在某些情况下,除了可在 上直接配置的属性之外,您可能还希望指定其他环境设置属性。您应该在 中设置此类属性,并在属性中引用它们。??context-source????Map????base-env-props-ref??

7.2. 配置??LdapTemplate??

通过使用元素定义。最简单的声明是元素本身:??LdapTemplate????<ldap:ldap-template>????ldap-template??

例 27。最简单的 ldap 模板声明

<ldap:ldap-template />

该元素本身创建一个具有默认 ID 的实例,引用默认值 ,该 ID 应为 (元素的默认值)。??LdapTemplate????ContextSource????contextSource????context-source??

下表描述了 上的可配置属性:??ldap-template??

表 2.ldap模板配置属性

属性

违约

描述

??id??

??ldapTemplate??

创建的 Bean 的 ID。

??context-source-ref??

??contextSource??

要使用的实例的 ID。??ContextSource??

??count-limit??

??0??

搜索的默认计数限制。0 表示无限制。

??time-limit??

??0??

搜索的默认时间限制(以毫秒为单位)。0 表示无限制。

??search-scope??

??SUBTREE??

搜索的默认搜索范围。有效值为:

??OBJECT????ONELEVEL????SUBTREE??

??ignore-name-not-found??

??false??

指定是否应在搜索中忽略 。设置此属性以使由无效搜索库引起的错误被静默吞噬。??NameNotFoundException????true??

??ignore-partial-result??

??false??

指定是否应在搜索中忽略。某些 LDAP 服务器在引用方面存在问题。这些通常应该自动遵循。但是,如果这不起作用,它会以 .设置此属性以提供解决此问题的方法。??PartialResultException????PartialResultException????true??

??odm-ref??

要使用的实例的 ID。默认值为默认配置的 。??ObjectDirectoryMapper????DefaultObjectDirectoryMapper??

7.3. 获取对基本 LDAP 路径的引用

如前所述,您可以为 提供基本 LDAP 路径,指定 LDAP 树中所有操作都与之相关的根。这意味着您在整个系统中仅使用相对可分辨的名称,这通常相当方便。但是,在某些情况下,您可能需要访问基本路径才能构建相对于 LDAP 树的实际根的完整 DN。例如,使用 LDAP 组(例如,对象类)时。在这种情况下,每个组成员属性值都需要是引用成员的完整 DN。??ContextSource????groupOfNames??

出于这个原因,Spring LDAP有一种机制,通过该机制,任何Spring控制的bean都可以在启动时提供基本路径。 要通知 bean 基本路径,需要做好两件事。首先,需要基路径引用的 Bean 需要实现接口。 其次,您需要在应用程序上下文中定义 a。 以下示例演示如何实现:??BaseLdapNameAware????BaseLdapPathBeanPostProcessor????BaseLdapNameAware??

例 28。实施??BaseLdapNameAware??

package com.example.service;public class PersonService implements PersonService, BaseLdapNameAware { … private LdapName basePath; public void setBaseLdapPath(LdapName basePath) { this.basePath = basePath; } … private LdapName getFullPersonDn(Person person) { return LdapNameBuilder.newInstance(basePath) .add(person.getDn()) .build(); } …}

下面的示例演示如何定义:??BaseLdapPathBeanPostProcessor??

例 29。在应用程序上下文中指定 BaseLdapPathBeanPostProcessor

<beans> … <ldap:context-source username=”cn=Administrator” password=”secret” url=”ldap://localhost:389″ base=”dc=261consulting,dc=com” /> … <bean class=”org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor” /></beans>

的默认行为是使用 中单个定义的 () 的基本路径。如果定义了多个,则需要通过设置属性来指定要使用的一个。??BaseLdapPathBeanPostProcessor????BaseLdapPathSource????AbstractContextSource????ApplicationContext????BaseLdapPathSource????baseLdapPathSourceName??

8. Spring LDAP 存储库

Spring LDAP内置了对Spring Data存储库的支持。此处介绍了基本功能和配置。使用 Spring LDAP 存储库时,您应该记住以下几点:

您可以通过在 XML 配置中使用元素或使用配置类上的注释来启用 Spring LDAP 存储库。<ldap:repositories>@EnableLdapRepositories要在自动生成的储存库中包括对参数的支持,请扩展接口而不是 。LdapQueryLdapRepositoryCrudRepository所有Spring LDAP存储库都必须使用带有ODM注释的实体,如对象目录映射(ODM)中所述。由于所有 ODM 托管类都必须将可分辨名称作为 ID,因此所有 Spring LDAP 存储库都必须将 ID 类型参数设置为 。 内置函数仅采用一个类型参数:托管实体类,默认 ID 为 。javax.naming.NameLdapRepositoryjavax.naming.Name由于LDAP协议的特殊性,Spring LDAP存储库不支持分页和排序。8.1. 查询DSL支持

基本查询DSL支持包含在Spring LDAP中。此支持包括以下内容:

一个注释处理器,称为 ,用于基于 Spring LDAP ODM 注释生成 QueryDSL 类。有关 ODM 注释的详细信息,请参阅对象目录映射 (ODM)。LdapAnnotationProcessor一个名为 的 Query 实现,用于在代码中生成和运行 QueryDSL 查询。QueryDslLdapQuerySpring 数据存储库支持 QueryDSL 谓词。 包括许多具有适当参数的附加方法。您可以扩展此接口,以将此支持包含在存储库中。QueryDslPredicateExecutorLdapRepository9. 池化支持

池化 LDAP 连接有助于减少为每个 LDAP 交互创建新的 LDAP 连接的开销。虽然存在Java LDAP 池支持,但它的配置选项和功能(如连接验证和池维护)受到限制。Spring LDAP 支持每个池的详细配置。??ContextSource??

池化支持是通过向应用程序上下文配置中的元素提供子元素来提供的。只读对象和读写对象分别池化(如果已指定)。Jakarta Commons-Pool用于提供底层池实现。??<ldap:pooling />????<ldap:context-source />????DirContext????anonymous-read-only??

9.1. 验证??DirContext??

验证池连接是使用自定义池库而不是 JDK 提供的 LDAP 池功能的主要动机。验证允许检查池连接,以确保在将池中签出连接、签入池中或在池中空闲时,它们仍处于正确连接和配置状态。??DirContext??

如果配置了连接验证,则使用 验证池连接。 执行 ,名称为空,筛选器为 ,并设置为仅使用属性和 500 毫秒超时限制单个结果。如果返回的结果为,则通过验证。如果未返回任何结果或引发异常,则验证失败。 默认设置应在大多数 LDAP 服务器上不更改配置的情况下工作,并提供验证 . 如果需要自定义,可以使用池配置中所述的验证??配置??属性来实现。??DefaultDirContextValidator????DefaultDirContextValidator????DirContext.search(String, String, SearchControls)????”objectclass=*”????SearchControls????objectclass????NamingEnumeration????DirContext????DirContext????DirContext??

如果连接引发被视为非暂时性的异常,则连接将自动失效。例如,如果实例抛出 ,则会将其解释为非暂时性错误,并且实例将自动失效,而不会产生额外操作的开销。解释为非暂时性的异常是使用 .??DirContext????javax.naming.CommunicationException????testOnReturn????nonTransientExceptions????PoolingContextSource??

9.2. 池配置

以下属性可用于配置目录上下文池的元素:??<ldap:pooling />??

表 3.池化配置属性

属性

违约

描述

??max-active??

??8??

可以同时从此池分配的每种类型(只读或读写)的最大活动连接数。您可以使用无限制的非正数。

??max-total??

??-1??

可以同时从此池分配的活动连接总数(适用于所有类型)。您可以使用无限制的非正数。

??max-idle??

??8??

每种类型(只读或读写)的最大活动连接数,这些连接可以在池中保持空闲状态,而不会释放额外的连接。您可以使用无限制的非正数。

??min-idle??

??0??

每种类型(只读或读写)的最小活动连接数,这些连接可以在池中保持空闲状态,而无需创建额外的连接。您可以使用零(默认值)创建无。

??max-wait??

??-1??

池在引发异常之前等待(当没有可用连接时)返回连接的最大毫秒数。您可以使用非正数无限期等待。

??when-exhausted??

??BLOCK??

指定池耗尽时的行为。

该选项在池耗尽时引发。??FAIL????NoSuchElementException??该选项将一直等到新对象可用。如果为正数,并且在时间到期后没有新对象可用,则抛出。??BLOCK????max-wait????max-wait????NoSuchElementException??该选项创建并返回一个新对象(实质上是毫无意义的)。??GROW????max-active??

??test-on-borrow??

??false??

在从池中借用对象之前是否验证对象。如果对象验证失败,则会将其从池中删除,并尝试借用另一个对象。

??test-on-return??

??false??

对象在返回到池之前是否经过验证。

??test-while-idle??

??false??

对象是否由空闲对象清除器验证(如果有)。如果对象验证失败,则会将其从池中删除。

??eviction-run-interval-millis??

??-1??

空闲对象 ereator 线程运行之间的休眠毫秒数。当为非正值时,不运行空闲对象清除器线程。

??tests-per-eviction-run??

??3??

每次运行空闲对象清除器线程(如果有)期间要检查的对象数。

??min-evictable-time-millis??

??1000 * 60 * 30??(30分钟)

对象在符合空闲对象迁移器逐出条件之前在池中空闲的最短时间(如果有)。

??validation-query-base??

??LdapUtils.emptyName()??

验证连接时要使用的搜索库。仅在指定 、 或 时使用。??test-on-borrow????test-on-return????test-while-idle??

??validation-query-filter??

??objectclass=*??

验证连接时要使用的搜索筛选器。仅在指定 、 或 时使用。??test-on-borrow????test-on-return????test-while-idle??

??validation-query-search-controls-ref??

??null??;默认搜索控件设置如上所述。

验证连接时要使用的实例的 ID。仅在指定了 、 或时使用。??SearchControls????test-on-borrow????test-on-return????test-while-idle??

??non-transient-exceptions??

??javax.naming.CommunicationException??

以逗号分隔的类列表。列出的例外在预先失效方面被认为是非暂时性的。如果对池实例的调用引发任何列出的异常(或其子类),则该对象将自动失效,而无需任何其他 testOnReturn 操作。??Exception????DirContext??

9.3. 池2配置

元素上提供了以下属性,用于配置池:??<ldap:pooling2 />????DirContext??

表 4.池化配置属性

属性

违约

描述

??max-total??

??-1??

可以同时从此池分配的活动连接总数(适用于所有类型)。您可以使用无限制的非正数。

??max-total-per-key??

??8??

每个键的池分配的对象实例数(已签出或空闲)的限制。达到限制时,子池将耗尽。负值表示没有限制。

??max-idle-per-key??

??8??

每种类型(只读或读写)的最大活动连接数,这些连接可以在池中保持空闲状态,而不会释放额外的连接。负值表示没有限制。

??min-idle-per-key??

??0??

每种类型(只读或读写)的最小活动连接数,这些连接可以在池中保持空闲状态,而无需创建额外的连接。您可以使用零(默认值)创建无。

??max-wait??

??-1??

池在引发异常之前等待(没有可用连接时)返回连接的最大毫秒数。您可以使用非正数无限期等待。

??block-when-exhausted??

??true??

是否等到新对象可用。如果 max-wait 为正,则在时间到期后没有新对象可用时抛出 a。??NoSuchElementException????maxWait??

??test-on-create??

??false??

借用前是否验证对象。如果对象验证失败,则借用失败。

??test-on-borrow??

??false??

指示对象在从池中借用之前是否经过验证的指示器。如果对象验证失败,则会将其从池中删除,并尝试借用另一个对象。

??test-on-return??

??false??

指示对象在返回到池之前是否经过验证的指示器。

??test-while-idle??

??false??

指示对象是否由空闲对象清除器(如果有)验证的指示器。如果对象验证失败,则会将其从池中删除。

??eviction-run-interval-millis??

??-1??

空闲对象 ereator 线程运行之间的休眠毫秒数。当为非正值时,不运行空闲对象清除器线程。

??tests-per-eviction-run??

??3??

每次运行空闲对象清除器线程(如果有)期间要检查的对象数。

??min-evictable-time-millis??

??1000 * 60 * 30??(30分钟)

对象在符合空闲对象迁移器逐出条件之前在池中空闲的最短时间(如果有)。

??soft-min-evictable-time-millis??

??-1??

对象在符合空闲对象迁移器逐出条件之前在池中可能处于空闲状态的最短时间,附加条件是每个键至少保留最少数量的对象实例。如果此设置设置为正值,则由 覆盖此设置。??min-evictable-time-millis??

??eviction-policy-class??

??org.apache.commons.pool2.impl.DefaultEvictionPolicy??

此池使用的逐出策略实现。池尝试使用线程上下文类加载器加载类。如果失败,池将尝试使用装入此类的类装入器装入类。

??fairness??

??false??

池提供等待公平借用连接的线程。 意味着等待线程就像在 FIFO 队列中等待一样。??true??

??jmx-enable??

??true??

JMX 通过池的平台 MBean 服务器启用。

??jmx-name-base??

??null??

用作分配给已启用 JMX 的池的名称的一部分的 JMX 名称库。

??jmx-name-prefix??

??pool??

用作分配给启用 JMX 的池的名称的一部分的 JMX 名称前缀。

??lifo??

??true??

指示池是否具有相对于空闲对象的 LIFO(后进先出)行为或作为 FIFO(先进先出)队列的指示器。LIFO 始终从池中返回最近使用的对象,而 FIFO 始终返回空闲对象池中最旧的对象

??validation-query-base??

??LdapUtils.emptyPath()??

用于验证搜索的基本 DN。

??validation-query-filter??

??objectclass=*??

用于验证查询的筛选器。

??validation-query-search-controls-ref??

??null??;默认搜索控件设置如上所述。

验证连接时要使用的实例的 ID。仅在指定 、 或 时使用??SearchControls????test-on-borrow????test-on-return????test-while-idle??

??non-transient-exceptions??

??javax.naming.CommunicationException??

以逗号分隔的类列表。列出的例外在预先失效方面被认为是非暂时性的。如果对池实例的调用引发任何列出的异常(或其子类),则该对象将自动失效,而无需任何其他 testOnReturn 操作。??Exception????DirContext??

9.4. 配置

配置池化需要添加嵌套在元素中的元素,如下所示:??<ldap:pooling>????<ldap:context-source>??

<beans> … <ldap:context-source password=”secret” url=”ldap://localhost:389″ username=”cn=Manager”> <ldap:pooling /> </ldap:context-source> …</beans>

在实际情况下,您可能会配置池选项并启用连接验证。前面的示例演示了一般思路。

9.4.1. 验证配置

下面的示例在将每个对象传递到客户端应用程序之前对其进行测试,并测试池中一直处于空闲状态的对象:??DirContext????DirContext??

<beans> … <ldap:context-source username=”cn=Manager” password=”secret” url=”ldap://localhost:389″ > <ldap:pooling test-on-borrow=”true” test-while-idle=”true” /> </ldap:context-source> …</beans>9.5. 已知问题

本节介绍人们使用 Spring LDAP 时有时会出现的问题。目前,它涵盖以下问题:

自定义身份验证9.5.1. 自定义认证

假定从中检索的所有对象都具有相同的环境,同样,从中检索的所有对象都具有相同的环境。这意味着用 in a 包装配置的 不会按预期运行。将使用第一个用户的凭据填充池,除非需要新连接,否则不会为请求线程指定的用户填充后续上下文请求。??PoolingContextSource????DirContext????ContextSource.getReadOnlyContext()????DirContext????ContextSource.getReadWriteContext()????LdapContextSource????AuthenticationSource????PoolingContextSource????AuthenticationSource??

10. 添加缺少的重载 API 方法

本节介绍如何添加自己的重载 API 方法以实现新功能。

10.1. 实现自定义搜索方法

??LdapTemplate??包含 中最常见操作的多个重载版本。但是,我们没有为每个方法签名提供替代方法,主要是因为它们太多了。但是,我们提供了一种调用您想要的任何方法的方法,并且仍然可以获得提供的好处。??DirContext????DirContext????LdapTemplate??

假设您要调用以下方法:??DirContext??

NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls ctls)

中没有相应的重载方法。解决此问题的方法是使用自定义实现,如下所示:??LdapTemplate????SearchExecutor??

public interface SearchExecutor { public NamingEnumeration executeSearch(DirContext ctx) throws NamingException;}

在自定义执行程序中,您可以访问一个对象,您可以使用该对象调用所需的方法。然后,您可以提供一个处理程序,负责映射属性和收集结果。例如,您可以使用 的可用实现之一,它将映射的结果收集到内部列表中。为了实际执行搜索,您需要调用将执行器和处理程序作为参数的方法。最后,您需要返回处理程序收集的任何内容。以下示例演示如何执行所有这些操作:??DirContext????CollectingNameClassPairCallbackHandler????search????LdapTemplate??

例 30。使用 和 的自定义搜索方法??SearchExecutor????AttributesMapper??

package com.example.repo;public class PersonRepoImpl implements PersonRepo { … public List search(final Name base, final String filter, final String[] params, final SearchControls ctls) { SearchExecutor executor = new SearchExecutor() { public NamingEnumeration executeSearch(DirContext ctx) { return ctx.search(base, filter, params, ctls); } }; CollectingNameClassPairCallbackHandler handler = new AttributesMapperCallbackHandler(new PersonAttributesMapper()); ldapTemplate.search(executor, handler); return handler.getList(); }}

如果您更喜欢 而不是 ,以下示例显示了它的外观:??ContextMapper????AttributesMapper??

例 31。使用 和 的自定义搜索方法??SearchExecutor????ContextMapper??

package com.example.repo;public class PersonRepoImpl implements PersonRepo { … public List search(final Name base, final String filter, final String[] params, final SearchControls ctls) { SearchExecutor executor = new SearchExecutor() { public NamingEnumeration executeSearch(DirContext ctx) { return ctx.search(base, filter, params, ctls); } }; CollectingNameClassPairCallbackHandler handler = new ContextMapperCallbackHandler(new PersonContextMapper()); ldapTemplate.search(executor, handler); return handler.getList(); }}

使用 时,必须确保已调用实例。??ContextMapperCallbackHandler????setReturningObjFlag(true)????SearchControls??

10.2. 实现其他自定义上下文方法

与自定义方法相同的方式,实际上可以使用 调用 中的任何方法,如下所示:??search????DirContext????ContextExecutor??

public interface ContextExecutor { public Object executeWithContext(DirContext ctx) throws NamingException;}

实现自定义时,您可以选择使用 或 方法。假设您要调用以下方法:??ContextExecutor????executeReadOnly()????executeReadWrite()??

Object lookupLink(Name name)

该方法在 中可用,但在 中没有匹配的方法。它是一种查找方法,因此应该是只读的。我们可以按如下方式实现它:??DirContext????LdapTemplate??

例 32。使用??DirContext????ContextExecutor??

package com.example.repo;public class PersonRepoImpl implements PersonRepo { … public Object lookupLink(final Name name) { ContextExecutor executor = new ContextExecutor() { public Object executeWithContext(DirContext ctx) { return ctx.lookupLink(name); } }; return ldapTemplate.executeReadOnly(executor); }}

同样,您可以使用该方法执行读写操作。??executeReadWrite()??

11. 处理??DirContext??

本节介绍如何处理 ,包括预处理和后处理。??DirContext??

11.1. 自定义预处理和后处理??DirContext??

在某些情况下,您可能希望对搜索操作之前和之后执行操作。用于此目的的接口称为 。以下清单显示了该接口:??DirContext????DirContextProcessor????DirContextProcessor??

public interface DirContextProcessor { public void preProcess(DirContext ctx) throws NamingException; public void postProcess(DirContext ctx) throws NamingException;}

该类有一个搜索方法,该方法采用 ,如下所示:??LdapTemplate????DirContextProcessor??

public void search(SearchExecutor se, NameClassPairCallbackHandler handler, DirContextProcessor processor) throws DataAccessException;

在执行搜索操作之前,将在给定实例上调用该方法。运行搜索并处理结果后,将调用该方法。这使您可以对要在搜索中使用的 执行操作,并检查何时执行搜索。这可能非常有用(例如,在处理请求和响应控件时)。??preProcess????DirContextProcessor????NamingEnumeration????postProcess????DirContext????DirContext??

当您不需要自定义时,您还可以使用以下方便的方法:??SearchExecutor??

public void search(Name base, String filter, SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)public void search(String base, String filter, SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)public void search(Name base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)public void search(String base, String filter, SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)public void search(Name base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor)public void search(String base, String filter, SearchControls controls, ContextMapper mapper, DirContextProcessor processor)11.2. 实现请求控制??DirContextProcessor??

LDAPv3 协议使用“控件”发送和接收其他数据,以影响预定义操作的行为。为了简化请求控制的实现,Spring LDAP提供了基类。此类处理从 检索当前请求控件的操作,调用用于创建请求控件的模板方法,并将其添加到 中。在子类中,您所要做的就是实现调用的模板方法以及用于执行搜索后需要执行的任何操作的方法。以下清单显示了相关的签名:??DirContextProcessor????AbstractRequestControlDirContextProcessor????LdapContext????LdapContext????createRequestControl????postProcess??

public abstract class AbstractRequestControlDirContextProcessor implements DirContextProcessor { public void preProcess(DirContext ctx) throws NamingException { … } public abstract Control createRequestControl();}

典型类似于以下示例:??DirContextProcessor??

例 33。请求控制实现??DirContextProcessor??

package com.example.control;public class MyCoolRequestControl extends AbstractRequestControlDirContextProcessor { private static final boolean CRITICAL_CONTROL = true; private MyCoolCookie cookie; … public MyCoolCookie getCookie() { return cookie; } public Control createRequestControl() { return new SomeCoolControl(cookie.getCookie(), CRITICAL_CONTROL); } public void postProcess(DirContext ctx) throws NamingException { LdapContext ldapContext = (LdapContext) ctx; Control[] responseControls = ldapContext.getResponseControls(); for (int i = 0; i < responseControls.length; i++) { if (responseControls[i] instanceof SomeCoolResponseControl) { SomeCoolResponseControl control = (SomeCoolResponseControl) responseControls[i]; this.cookie = new MyCoolCookie(control.getCookie()); } } }}

确保在使用控件时使用。控制?接口特定于 LDAPv3,需要使用 代替 。如果调用子类的参数不是 ,则会抛出 .??LdapContextSource????LdapContext????DirContext????AbstractRequestControlDirContextProcessor????LdapContext????IllegalArgumentException??

11.3. 分页搜索结果

某些搜索可能会返回大量结果。当没有简单的方法可以过滤掉较小的数量时,让服务器在每次调用时只返回一定数量的结果会很方便。这称为“分页搜索结果”。然后可以显示结果的每个“页面”,并带有指向下一页和上一页的链接。如果没有此功能,客户端必须手动将搜索结果限制为页面,或者检索整个结果,然后将其切成适当大小的页面。前者相当复杂,后者会消耗不必要的内存量。

某些 LDAP 服务器支持 ,它请求 LDAP 服务器以指定大小的页面返回搜索操作的结果。用户通过控制调用搜索的速率来控制返回页面的速率。但是,您必须在调用之间跟踪 cookie。服务器使用此 cookie 来跟踪上次使用分页结果请求调用它时中断的位置。??PagedResultsControl??

Spring LDAP 通过使用 的预处理和后处理的概念来提供对分页结果的支持,如前面的章节所述。它通过使用类来实现此目的。该类创建具有请求页面大小的 ,并将其添加到 .搜索后,它会获取并检索分页结果 cookie,这是在连续分页结果请求之间保持上下文所必需的。??LdapContext????PagedResultsDirContextProcessor????PagedResultsDirContextProcessor????PagedResultsControl????LdapContext????PagedResultsResponseControl??

下面的示例演示如何使用分页搜索结果功能:

例 34。分页结果使用??PagedResultsDirContextProcessor??

public List<String> getAllPersonNames() { final SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); final PagedResultsDirContextProcessor processor = new PagedResultsDirContextProcessor(PAGE_SIZE); return SingleContextSource.doWithSingleContext( contextSource, new LdapOperationsCallback<List<String>>() { @Override public List<String> doWithLdapOperations(LdapOperations operations) { List<String> result = new LinkedList<String>(); do { List<String> oneResult = operations.search( “ou=People”, “(&(objectclass=person))”, searchControls, CN_ATTRIBUTES_MAPPER, processor); result.addAll(oneResult); } while(processor.hasMore()); return result; } });}

要使分页结果 Cookie 继续有效,必须对每个分页结果调用使用相同的基础连接。可以使用 来执行此操作,如前面的示例所示。??SingleContextSource??

12. 交易支持

习惯于使用关系数据库的程序员来到LDAP世界,经常对没有事务概念的事实表示惊讶。 协议中未指定它,并且没有 LDAP 服务器支持它。 认识到这可能是一个主要问题,Spring LDAP为客户端提供了支持,补偿LDAP资源上的事务。

LDAP 事务支持由 提供,这是一个管理 Spring 事务对 LDAP 操作的支持的实现。它与协作者一起跟踪事务中执行的LDAP操作,在每次操作之前记录状态,并在事务需要回滚时采取措施恢复初始状态。??ContextSourceTransactionManager????PlatformTransactionManager??

除了实际的事务管理之外,Spring LDAP事务支持还确保在同一事务中使用相同的实例。也就是说,在事务完成之前,实际上不会关闭,从而可以更有效地使用资源。??DirContext????DirContext??

虽然Spring LDAP用于提供事务支持的方法在许多情况下已经足够了,但它绝不是传统意义上的“真实”事务。 服务器完全不知道事务,因此(例如),如果连接断开,则无法回滚事务。 虽然应该仔细考虑这一点,但也应该注意,另一种选择是在没有任何交易支持的情况下进行操作。Spring LDAP的事务支持几乎和它得到的一样好。

除了原始操作所需的工作之外,客户端事务支持还增加了一些开销。 虽然在大多数情况下,这种开销不应该是值得担心的事情, 如果应用程序未在同一事务中执行多个 LDAP 操作(例如,后跟 ), 或者,如果不需要与 JDBC 数据源进行事务同步(请参阅JDBC 事务集成?),则使用 LDAP 事务支持几乎没有什么好处。??modifyAttributes????rebind??

12.1. 配置

如果您习惯于配置 Spring 事务,那么配置 Spring LDAP 事务应该看起来非常熟悉。您可以使用 对事务处理类进行注释,创建一个实例,并在 Bean 配置中包含一个元素。以下示例演示如何执行此操作:??@Transactional????TransactionManager????<tx:annotation-driven>??

<ldap:context-source url=”ldap://localhost:389″ base=”dc=example,dc=com” username=”cn=Manager” password=”secret” /><ldap:ldap-template id=”ldapTemplate” /><ldap:transaction-manager> <!– Note this default configuration will not work for more complex scenarios; see below for more information on RenamingStrategies. –> <ldap:default-renaming-strategy /></ldap:transaction-manager><!– The MyDataAccessObject class is annotated with @Transactional.–><bean id=”myDataAccessObject” class=”com.example.MyRepository”> <property name=”ldapTemplate” ref=”ldapTemplate” /></bean><tx:annotation-driven />…

虽然此设置适用于大多数简单用例,但一些更复杂的场景需要额外的配置。 具体来说,如果需要在事务中创建或删除子树,则需要使用替代方法,如重命名策略?中所述。??TempEntryRenamingStrategy??

在实际情况下,您可能会在服务对象级别而不是存储库级别应用事务。前面的示例演示了一般思路。

12.2. JDBC 事务集成

使用 LDAP 时的一个常见用例是,某些数据存储在 LDAP 树中,而其他数据存储在关系数据库中。在这种情况下,事务支持变得更加重要,因为不同资源的更新应该同步。

虽然不支持实际的 XA 事务,但通过向元素提供属性,支持在概念上将 JDBC 和 LDAP 访问包装在同一事务中。这将创建一个 ,然后它几乎像管理一个事务一样管理两个事务。执行提交时,操作的 LDAP 部分始终首先执行,如果 LDAP 提交失败,则允许回滚两个事务。事务的 JDBC 部分的管理与 中的完全相同,只是不支持嵌套事务。下面的示例演示具有属性的元素:??data-source-ref????<ldap:transaction-manager>????ContextSourceAndDataSourceTransactionManager????DataSourceTransactionManager????ldap:transaction-manager????data-source-ref??

<ldap:transaction-manager data-source-ref=”dataSource” > <ldap:default-renaming-strategy /><ldap:transaction-manager />

提供的所有支持都是客户端的。 包装的事务不是 XA 事务。不执行两阶段提交,因为 LDAP 服务器无法对其结果进行投票。

您可以通过向元素提供属性来完成 Hibernate 集成的相同操作,如下所示:??session-factory-ref????<ldap:transaction-manager>??

<ldap:transaction-manager session-factory-ref=”dataSource” > <ldap:default-renaming-strategy /><ldap:transaction-manager />12.3. LDAP 补偿交易说明

Spring LDAP 通过在每次修改操作(、、、、和)之前在 LDAP 树中记录状态来管理补偿事务。 这允许系统在事务需要回滚时执行补偿操作。??bind????unbind????rebind????modifyAttributes????rename??

在许多情况下,补偿操作非常简单。例如,操作的补偿回滚操作是取消绑定条目。 但是,由于 LDAP 数据库的某些特定特征,其他操作需要不同的、更复杂的方法。 具体来说,并不总是能够获取所有条目的值,这使得上述策略不足以(例如)操作。??bind????Attributes????unbind??

这就是为什么在Spring LDAP管理事务中执行的每个修改操作在内部被拆分为四个不同的操作:记录操作, 准备操作、提交操作和回滚操作。下表描述了每个 LDAP 操作:

操作

录音

制备

反转

??bind??

记录要绑定的条目的 DN。

绑定条目。

无操作。

使用记录的 DN 取消绑定条目。

??rename??

记录原始 DN 和目标 DN。

重命名条目。

无操作。

将条目重命名回其原始 DN。

??unbind??

记录原始 DN 并计算临时 DN。

将条目重命名为临时位置。

取消绑定临时条目。

将条目从临时位置重命名回其原始 DN。

??rebind??

记录原始 DN 和新 DN,并计算临时 DN。??Attributes??

将条目重命名为临时位置。

在原始 DN 处绑定新条目,并从其临时位置取消绑定原始条目。??Attributes??

将条目从临时位置重命名回其原始 DN。

??modifyAttributes??

记录条目的 DN 以进行修改,并计算要完成的修改的补偿实例。??ModificationItem??

执行操作。??modifyAttributes??

无操作。

使用计算的补偿实例执行操作。??modifyAttributes????ModificationItem??

有关Spring LDAP事务支持的内部工作原理的更详细描述,请参见Javadoc。

12.3.1. 重命名策略

如上一节的表所述,某些操作的事务管理需要先暂时重命名受操作影响的原始条目,然后才能在提交中进行实际修改。计算条目的临时 DN 的方式由配置中声明的子元素中指定的 管理。Spring LDAP 包括两个实现:??TempEntryRenamingStrategy????<ldap:transaction-manager >??

??DefaultTempEntryRenamingStrategy??(默认值):使用元素指定。将后缀添加到条目 DN 的最低有效部分。例如,对于 的 DN,此策略返回临时 DN 的 。您可以通过设置属性来配置后缀。<ldap:default-renaming-strategy />cn=john doe, ou=userscn=john doe_temp, ou=userstemp-suffix??DifferentSubtreeTempEntryRenamingStrategy??:使用元素指定。它将子树 DN 附加到 DN 的最低重要部分。这样做会使所有临时条目都放置在 LDAP 树中的特定位置。临时子树 DN 是通过设置属性来配置的。例如,如果 是 并且条目的原始 DN 为 ,则临时 DN 为 。请注意,配置的子树节点需要存在于 LDAP 树中。<ldap:different-subtree-renaming-strategy />subtree-nodesubtree-nodeou=tempEntriescn=john doe, ou=users??cn=john doe, ou=tempEntries??13. 使用 Spring LDAP 进行用户身份验证

本节介绍使用 Spring LDAP 进行用户身份验证。它包含以下主题:

基本身份验证对经过身份验证的上下文执行操作过时的身份验证方法使用弹簧安全性13.1. 基本身份验证

虽然 的核心功能是提供实例供 使用,但您也可以使用它来针对 LDAP 服务器对用户进行身份验证。方法正是这样做的。它根据配置构造实例,并使用提供的主体和凭据对上下文进行身份验证。自定义身份验证方法可能类似于以下示例:??ContextSource????DirContext????LdapTemplate????getContext(principal, credentials)????ContextSource????DirContext????ContextSource??

public boolean authenticate(String userDn, String credentials) { DirContext ctx = null; try { ctx = contextSource.getContext(userDn, credentials); return true; } catch (Exception e) { // Context creation failed – authentication did not succeed logger.error(“Login failed”, e); return false; } finally { // It is imperative that the created DirContext instance is always closed LdapUtils.closeContext(ctx); }}

提供给方法的 需要是要进行身份验证的用户的完整 DN(无论 上的设置如何)。您通常需要根据(例如)用户名执行 LDAP 搜索才能获取此 DN。以下示例演示如何执行此操作:??userDn????authenticate????base????ContextSource??

private String getDnForUser(String uid) { List<String> result = ldapTemplate.search( query().where(“uid”).is(uid), new AbstractContextMapper() { protected String doMapFromContext(DirContextOperations ctx) { return ctx.getNameInNamespace(); } }); if(result.size() != 1) { throw new RuntimeException(“User not found or not unique”); } return result.get(0);}

这种方法有一些缺点。您被迫关注用户的 DN,您只能搜索用户的 uid,并且搜索始终从树的根(空路径)开始。更灵活的方法允许您指定搜索库、搜索筛选器和凭据。Spring LDAP 包含一个提供此功能的身份验证方法:。??LdapTemplate????boolean authenticate(LdapQuery query, String password);??

使用此方法时,身份验证变得非常简单,如下所示:

例 35。使用 Spring LDAP 对用户进行身份验证

ldapTemplate.authenticate(query().where(“uid”).is(“john.doe”), “secret”);

如下一节?所述,某些设置可能需要执行其他操作才能进行实际身份验证。有关详细信息,请参阅对经过身份验证的上下文执行操作。

不要编写自己的自定义身份验证方法。使用Spring LDAP中提供的那些。

13.2. 在经过身份验证的上下文上执行操作

某些身份验证方案和 LDAP 服务器需要在创建的实例上执行某些操作才能进行实际身份验证。您应该测试并确保服务器设置和身份验证方案的行为方式。如果不这样做,则可能导致用户被允许进入您的系统,而不管提供的 DN 和凭据如何。以下示例显示了身份验证方法的朴素实现,其中对经过身份验证的上下文执行硬编码操作:??DirContext????lookup??

public boolean myAuthenticate(String userDn, String credentials) { DirContext ctx = null; try { ctx = contextSource.getContext(userDn, credentials); // Take care here – if a base was specified on the ContextSource // that needs to be removed from the user DN for the lookup to succeed. ctx.lookup(userDn); return true; } catch (Exception e) { // Context creation failed – authentication did not succeed logger.error(“Login failed”, e); return false; } finally { // It is imperative that the created DirContext instance is always closed LdapUtils.closeContext(ctx); }}

如果可以将操作作为回调接口的实现提供,而不是将操作限制为始终为 .Spring LDAP 包含回调接口和相应的方法:??lookup????AuthenticatedLdapEntryContextMapper????authenticate????<T> T authenticate(LdapQuery query, String password, AuthenticatedLdapEntryContextMapper<T> mapper);??

此方法允许对经过身份验证的上下文执行任何操作,如下所示:

例 36。使用 Spring LDAP 在经过身份验证的上下文上执行 LDAP 操作

AuthenticatedLdapEntryContextMapper<DirContextOperations> mapper = new AuthenticatedLdapEntryContextMapper<DirContextOperations>() { public DirContextOperations mapWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) { try { return (DirContextOperations) ctx.lookup(ldapEntryIdentification.getRelativeName()); } catch (NamingException e) { throw new RuntimeException(“Failed to lookup ” + ldapEntryIdentification.getRelativeName(), e); } }};ldapTemplate.authenticate(query().where(“uid”).is(“john.doe”), “secret”, mapper);13.3. 过时的身份验证方法

除了前面各节中介绍的方法之外,还可以使用许多已弃用的方法进行身份验证。虽然这些方法工作正常,但我们建议改用这些方法。??authenticate????LdapQuery??

13.4. 使用 Spring 安全性

虽然前面各节中介绍的方法可能足以满足简单的身份验证方案,但此领域的要求通常会迅速扩展。许多方面都适用,包括身份验证、授权、Web 集成、用户上下文管理等。如果您怀疑这些要求可能会扩展到简单的身份验证之外,那么您绝对应该考虑将Spring 安全性用于您的安全目的。它是一个功能齐全、成熟的安全框架,可解决上述方面以及其他几个方面。

14. LDIF 解析

LDAP 目录交换格式 (LDIF) 文件是以平面文件格式描述目录数据的标准介质。这种格式最常见的用途包括信息传输和存档。但是,该标准还定义了一种以平面文件格式描述对存储数据的修改的方法。此更高类型的 LDIF 通常称为更改类型或修改LDIF。

该包提供了解析 LDIF 文件并将其反序列化为有形对象所需的类。它是包的主类,能够解析符合 RFC 2849 的文件。此类从资源中读取行并将它们组合到对象中。??org.springframework.ldap.ldif????LdifParser????org.springframework.ldap.ldif????LdapAttributes??

当前忽略更改类型LDIF 条目,因为它们在应用程序上下文中的有用性尚未确定。??LdifParser??

14.1. 对象表示

包中的两个类提供了在代码中表示 LDIF 的方法:??org.springframework.ldap.core??

??LdapAttribute??:扩展了对 RFC2849 中定义的 LDIF 选项的支持。javax.naming.directory.BasicAttribute??LdapAttributes??:扩展了添加对 DN 的专用支持。javax.naming.directory.BasicAttributes

??LdapAttribute??对象将选项表示为 .添加到对象的 DN 支持使用该类。??Set<String>????LdapAttributes????javax.naming.ldap.LdapName??

14.2. 解析器

该接口为操作提供了基础,并采用了三个支持策略定义:??Parser??

??SeparatorPolicy??:建立将行组合成属性的机制。??AttributeValidationPolicy??:确保在解析之前属性结构正确。??Specification??:提供一种机制,通过该机制可以在组装后验证对象结构。

这些接口的默认实现如下所示:

??org.springframework.ldap.ldif.parser.LdifParser????org.springframework.ldap.ldif.support.SeparatorPolicy????org.springframework.ldap.ldif.support.DefaultAttributeValidationPolicy????org.springframework.ldap.schema.DefaultSchemaSpecification??

这四个类一起逐行解析资源并将数据转换为对象。??LdapAttributes??

确定应如何解释从源文件中读取的各个行,因为 LDIF 规范允许属性跨越多行。默认策略根据读取行的顺序评估行,以确定所考虑行的性质。控件属性和更改类型记录将被忽略。??SeparatorPolicy??

使用 REGEX 表达式来确保每个属性在解析后都符合有效的属性格式(根据 RFC 2849)。如果属性验证失败,则会记录 ,并跳过该记录(解析器返回 )。??DefaultAttributeValidationPolicy????InvalidAttributeFormatException????null??

14.3. 模式验证

通过包中的接口,可以使用一种机制来验证针对架构分析的对象。不执行任何验证,可用于已知记录有效且无需检查的情况。此选项可节省验证带来的性能损失。应用基本检查,例如确保已提供 DN 和对象类声明。目前,针对实际架构的验证需要实现接口。??Specification????org.springframework.ldap.schema????DefaultSchemaSpecification????BasicSchemaSpecification????Specification??

14.4. 弹簧批量集成

虽然任何需要解析LDIF文件的应用程序都可以使用它,但Spring提供了一个批处理框架,该框架提供了许多文件处理实用程序来解析分隔文件,例如CSV。该软件包提供了在 Spring Batch 框架中使用 作为有效配置选项所需的类。 此包中有五个类。它们共同提供了三个基本用例:??LdifParser????org.springframework.ldap.ldif.batch????LdifParser??

从文件中读取 LDIF 记录并返回对象。LdapAttributes从文件中读取LDIF记录并将记录映射到Java对象(POJO)。将 LDIF 记录写入文件。

第一个用例是通过 完成的。此类扩展了 Spring Batch 并实现其 .它自然适合框架,您可以使用它从文件中读取对象。??LdifReader????AbstractItemCountingItemStreamItemReader????ResourceAwareItemReaderItemStream????LdapAttributes??

您可以使用 将 LDIF 对象直接映射到任何 POJO。此类要求您提供接口的实现。此实现应实现将对象映射到 POJO 的逻辑。??MappingLdifReader????RecordMapper??

您可以实现并向任一读者提供实现。您可以使用此处理程序对跳过的记录进行操作。有关更多信息,请参阅Spring Batch API 文档。??RecordCallbackHandler??

此包的最后一个成员 可用于将 LDIF 记录写入文件。此类调用对象的方法。??LdifAggregator????toString()????LdapAttributes??

15. 实用程序

本节介绍可用于 Spring LDAP 的其他实用程序。

15.1. 多值属性的增量检索

当特定属性存在大量属性值 (>1500) 时,Active Directory 通常拒绝一次返回所有这些值。而是根据增量检索多值属性方法返回属性值。这样做需要调用部分检查返回的属性中是否有特定标记,并在必要时发出其他查找请求,直到找到所有值。

Spring LDAP 在处理此类属性时很有帮助,如下所示:??org.springframework.ldap.core.support.DefaultIncrementalAttributesMapper??

Object[] attrNames = new Object[]{“oneAttribute”, “anotherAttribute”};Attributes attrs = DefaultIncrementalAttributeMapper.lookupAttributes(ldapTemplate, theDn, attrNames);

前面的示例分析任何返回的属性范围标记,并根据需要发出重复请求,直到检索到所有请求属性的所有值。

16. 测试

本节介绍使用 Spring LDAP 进行测试。它包含以下主题:

使用嵌入式服务器阿帕奇DS未绑定标识16.1. 使用嵌入式服务器

??spring-ldap-test??提供基于ApacheDS或UnboundID的嵌入式 LDAP 服务器。

??spring-ldap-test??与 ApacheDS 1.5.5 兼容。不支持较新版本的 ApacheDS。

若要开始,需要包含依赖项。??spring-ldap-test??

以下清单显示了如何包含 for Maven:??spring-ldap-test??

<dependency> <groupId>org.springframework.ldap</groupId> <artifactId>spring-ldap-test</artifactId> <version>{project-version}</version> <scope>test</scope></dependency>

以下清单显示了如何包含 for Gradle:??spring-ldap-test??

testCompile “org.springframework.ldap:spring-ldap-test:{project-version}”16.2. 阿帕奇DS

要使用 ApacheDS,您需要包含许多 ApacheDS 依赖项。

以下示例显示了如何包含 Maven 的 ApacheDS 依赖项:

<dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-core</artifactId> <version>1.5.5</version> <scope>test</scope></dependency><dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-core-entry</artifactId> <version>1.5.5</version> <scope>test</scope></dependency><dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-protocol-shared</artifactId> <version>1.5.5</version> <scope>test</scope></dependency><dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-protocol-ldap</artifactId> <version>1.5.5</version> <scope>test</scope></dependency><dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-server-jndi</artifactId> <version>1.5.5</version> <scope>test</scope></dependency><dependency> <groupId>org.apache.directory.shared</groupId> <artifactId>shared-ldap</artifactId> <version>0.9.15</version> <scope>test</scope></dependency>

以下示例显示了如何包含 Gradle 的 ApacheDS 依赖项:

testCompile “org.apache.directory.server:apacheds-core:1.5.5”, “org.apache.directory.server:apacheds-core-entry:1.5.5”, “org.apache.directory.server:apacheds-protocol-shared:1.5.5”, “org.apache.directory.server:apacheds-protocol-ldap:1.5.5”, “org.apache.directory.server:apacheds-server-jndi:1.5.5”, “org.apache.directory.shared:shared-ldap:0.9.15”

以下 Bean 定义创建嵌入式 LDAP 服务器:

<bean id=”embeddedLdapServer” class=”org.springframework.ldap.test.EmbeddedLdapServerFactoryBean”> <property name=”partitionName” value=”example”/> <property name=”partitionSuffix” value=”dc=261consulting,dc=com” /> <property name=”port” value=”9321″ /></bean>

??spring-ldap-test??提供一种使用 填充 LDAP 服务器的机制。要使用它,请创建一个类似于以下内容的 Bean:??org.springframework.ldap.test.LdifPopulator??

<bean class=”org.springframework.ldap.test.LdifPopulator” depends-on=”embeddedLdapServer”> <property name=”contextSource” ref=”contextSource” /> <property name=”resource” value=”classpath:/setup_data.ldif” /> <property name=”base” value=”dc=jayway,dc=se” /> <property name=”clean” value=”true” /> <property name=”defaultBase” value=”dc=jayway,dc=se” /></bean>

针对嵌入式 LDAP 服务器的另一种方法是使用 ,如下所示:??org.springframework.ldap.test.TestContextSourceFactoryBean??

<bean id=”contextSource” class=”org.springframework.ldap.test.TestContextSourceFactoryBean”> <property name=”defaultPartitionSuffix” value=”dc=jayway,dc=se” /> <property name=”defaultPartitionName” value=”jayway” /> <property name=”principal” value=”uid=admin,ou=system” /> <property name=”password” value=”secret” /> <property name=”ldifFile” value=”classpath:/setup_data.ldif” /> <property name=”port” value=”1888″ /></bean>

此外,还提供了以编程方式使用嵌入式 LDAP 服务器的方法。??org.springframework.ldap.test.LdapTestUtils??

16.3. 未绑定ID

若要使用 UnboundID,需要包含 UnboundID 依赖项。

以下示例演示如何包含 Maven 的 UnboundID 依赖项:

<dependency> <groupId>com.unboundid</groupId> <artifactId>unboundid-ldapsdk</artifactId> <version>3.1.1</version> <scope>test</scope></dependency>

以下示例展示了如何为 Gradle 包含 UnboundID 依赖项:

testCompile “com.unboundid:unboundid-ldapsdk:3.1.1”

以下 Bean 定义创建嵌入式 LDAP 服务器:

<bean id=”embeddedLdapServer” class=”org.springframework.ldap.test.unboundid.EmbeddedLdapServerFactoryBean”> <property name=”partitionName” value=”example”/> <property name=”partitionSuffix” value=”dc=261consulting,dc=com” /> <property name=”port” value=”9321″ /></bean>

??spring-ldap-test??提供了一种使用 填充 LDAP 服务器的方法。要使用它,请创建一个类似于以下内容的 Bean:??org.springframework.ldap.test.unboundid.LdifPopulator??

<bean class=”org.springframework.ldap.test.unboundid.LdifPopulator” depends-on=”embeddedLdapServer”> <property name=”contextSource” ref=”contextSource” /> <property name=”resource” value=”classpath:/setup_data.ldif” /> <property name=”base” value=”dc=jayway,dc=se” /> <property name=”clean” value=”true” /> <property name=”defaultBase” value=”dc=jayway,dc=se” /></bean>

针对嵌入式 LDAP 服务器的另一种方法是使用 . 要使用它,请创建一个类似于以下内容的 Bean:??org.springframework.ldap.test.unboundid.TestContextSourceFactoryBean??

<bean id=”contextSource” class=”org.springframework.ldap.test.unboundid.TestContextSourceFactoryBean”> <property name=”defaultPartitionSuffix” value=”dc=jayway,dc=se” /> <property name=”defaultPartitionName” value=”jayway” /> <property name=”principal” value=”uid=admin,ou=system” /> <property name=”password” value=”secret” /> <property name=”ldifFile” value=”classpath:/setup_data.ldif” /> <property name=”port” value=”1888″ /></bean>

此外,提供以编程方式使用嵌入式 LDAP 服务器的方法。??org.springframework.ldap.test.unboundid.LdapTestUtils??

生命不息,奋斗不止,就像我们常说的一句话;

Spring LDAP参考(二)

相关文章:

你感兴趣的文章:

标签云: