1-5章的翻译见:https://www.jianshu.com/p/77517e26a357
说明
该文档根据官方英文文档翻译而来,该文档是对spring-ldap-core的2.3.2.RELEASE版本的介绍。翻译如下。
Spring LDAP 参考文档
Mattias Hellborg Arthursson,Ulrik Sandberg,Eric Dalquist,Keith Barlow,Rob Winch。2.3.2.RELEASE版本。
Spring LDAP使得我们构建基于Spring的那些使用了LDAP协议的应用变得简单
假如你不把本文档设为收费阅读,甚至你在提供本文内容时在文档中包含此版权说明,那你可以以纸质版或电子版的形式拷贝本文的副本来自己使用或做他用
6. 配置
6.1 前言
推荐配置Spring LDAP的方式是使用在XML中引入Spring LDAP配置的命名空间。我们需要将Spring
LDAP命名空间的声明引入我们的bean文件中,例如:
<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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
6.2. 配置ContextSource
ContextSource 通过使用ldap:context-source标签来配置。最简单的context-source配置值需要我们提供服务器url,username以及password:
最简单的context-source配置
<ldap:context-source
username="cn=Administrator"
password="secret"
url="ldap://localhost:389" />
这将会创建一个默认的LdapContextSource (参见下面的表格)以及指定的URL和身份验证凭据。context-source所有可配置的属性如下表,其中带*的是必需配置的属性:
表1. ContextSource 的配置属性
Attribute | Default | Description |
---|---|---|
id | contextSource | 被创建的bean的id |
username | 当验证LDAP服务器的时候需要用到username。一般是admin用户的DN值(例如:cn=Administrator),但是也可能会由于不同的服务器与认证方式而有差异。当authentication-source-ref没有显示的配置的时候,username是必需的 | |
password | 当验证LDAP服务器的时候需要用到password。当authentication-source-ref没有显示的配置的时候,password是必需的 | |
url* | LDAP服务器的URL。URL应该是如下的格式:ldap://myserver.example.com:389 。当使用SSL认证时,使用ldaps协议与核实的端口,如:ldaps://myserver.example.com:636 。如果需要故障转移(fail-over)功能,可以配置多个URL,URL之间使用逗号“,”分隔 |
|
base | LdapUtils.emptyLdapName() | base DN。当该属性配置之后,所有从LDAP中获取的DN或增加到LDAP的DN都会根据该base DN来操作。这可以大大简化针对LDAP树的操作。然而仍然有在某些情况下你需要访问base Path。想要知道关于这个的更多信息,请参阅Obtaining a reference to the base LDAP path |
anonymous-read-only | false | 用于定义一个匿名用户(未登录用户)是否有只读的操作权限。注意不支持将该配置设置为true的同时开启事务补偿。 |
referral | null | 定义处理referral的策略,这里有详细的描述。允许的值有:ignore follow throw |
native-pooling | false | 指出本地Java LDAP连接池是否启用。如果要使用Spring LDAP连接池,请参阅Pooling Support |
authentication-source-ref | A SimpleAuthenticationSource instance | 要使用的AuthenticationSource 实例的id(详情见下文) |
authentication-strategy-ref | A SimpleDirContextAuthenticationStrategy instance. | 要使用的SimpleDirContextAuthenticationStrategy 实例的id(详情见下文) |
base-env-props-ref | 指向一个存储环境属性的一个map。这个map会在DirContext 构建是传入。 |
6.2.1 认证DirContext
当DirContext 实例被创建后,在LDAP服务器中进行操作的时候这些contexts需要进行认证。使用Spring LDAP配置时有许多不同的选项。
这部分引用了ContextSource 核心功能中的authenticating contexts。LdapTemplate可以使用ContextSource来构造DirContext 实例来使用。LDAP通常仅仅用作用户的验证(
译者注:如作为用户的登录验证
),ContextSource 也可以用来做这个。这一过程在User Authentication using Spring LDAP
中有详细描述。
验证过的contexts创建后默认拥有只读和读写的操作。你可以在context-source基础上获取LDAP中的用户的username 与 password 属性来做用户验证的事情。
如果username是LDAP用户的DN值,不管在context-source中是否指定了base属性,username应该是从LDAP树的根节点开始完整的DN值。
某些LDAP服务器可以设置为匿名用户可以拥有只读的权限,如果你想使用匿名的Context来做只读的操作,可以将配置 anonymous-read-only 设置为true。
自定义DirContext认证处理(Custom DirContext Authentication Processing
)
翻译略 大意是说默认的认证方式是SIMPLE
。该方式将username 与 password放入Hashtable传给DirContext的构造器。然而有些LDAP服务器只允许安全的TLS通道的通信,这可能就需要使用特定的LDAP代理机制(LDAP Proxy Auth ),这就需要提供一个DirContextAuthenticationStrategy 的实现类,并在context-source中配置authentication-strategy-ref 属性。
TLS
翻译略 大意是说Spring LDAP针对TLS的认证策略提供了两种方案DefaultTlsDirContextAuthenticationStrategy and ExternalTlsDirContextAuthenticationStrategy。DefaultTlsDirContextAuthenticationStrategy 这个default开头的方案就是在TLS安全通道内使用username 与password来简单的认证,而external开头的方案在TLS通道内使用EXTERNAL SASL 认证方式。第二段说了不同的服务器对通道的关闭方式的响应不同,有些要求优雅的关闭(shutdown gracefully)。DirContextAuthenticationStrategy提供 了shutdownTlsGracefully 参数,如果为false (默认值)将会显式的关闭TLS,若为true,则会在关闭context之前尝试优雅的关闭TLS。
Custom Principal and Credentials ManagementUsing the(标题不全让我怎么翻译-_-||)
翻译略 大意是我们配置了username与password来创建了context后,就会一直使用这个用户来操作。这在某些情况下很不方便或者不符合业务要求。一个替代方案是在配置context-source时,将authentication-source-ref属性配置为指向一个AuthenticationSource 的实现类。这样每当Context 被创建时,都会由ContextSource 调用AuthenticationSource 来获取认证信息。
当使用Spring Security时,你可以将配置中的ContextSource 用SpringSecurityAuthenticationSource 来代替,以便能够保证使用用户名密码来认证当前登录的用户。
*使用SpringSecurityAuthenticationSource *
<beans>
...
<ldap:context-source
url="ldap://localhost:389"
authentication-source-ref="springSecurityAuthenticationSource/>
<bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" />
...
</beans>
当使用AuthenticationSource 时,就无需在context-source中指定用户名和密码了。
当使用SpringSecurityAuthenticationSource 时,你需要使用Spring Security的LdapAuthenticationProvider 来从LDAP中认证用户。
6.2.2. Java LDAP池
翻译略 Java LDAP内置提供了一些基础的连接池的能力,可以在AbstractContextSource中的pooled 属性中设置开关。自1.3版本后,该属性默认为false,也就是说Java LDAP池默认关闭。LDAP连接池使用了System 属性,并不在Spring Context configuration配置中,因此需要手动处理。详情可以看这里
内置的LDAP连接池有许多缺点,这既是为什么Spring LDAP要提供一个更精致的LDAP连接池技术,如果你需要连接池的功能,我们推荐使用Spring LDAP的连接池技术,更多信息参阅Pooling Support
为了使重置密码功能尽快生效,无论连接池怎么配置,ContextSource#getContext(String principal, String credentials)方法将不会使用Java LDAP连接池。
6.2.3. 高级ContextSource 配置
自定义DirContext 环境参数
在某些情况下用户可能需要在context-source中指定除默认配置外的额外的启动环境参数。这些属性可以放入到map中并被base-env-props-ref参数引用。
6.3. LdapTemplate 配置
翻译略
6.4. 获取base LDAP path
就像上文说说的,ContextSource可以获得一个base Path来指定所有操作的相对路径。这意味着你只能在系统中使用相对DN来操作,这当然相当方便。然而,在某些情况下,你可能需要获取到base Path的值来组装一个从LDAP 根节点开始的绝对DN值。一个直观的例子就是:当我们操作LDAP群组时,群组中的member属性值必须是指向某entry的绝对DN值。
由于以上原因,Spring LDAP提供了一个机制:任何Spring控制的bean都可以在启动时获取到base Path。对于bean来说要获取base Path,有两点要注意:第一,想要获取base Path的bean必须实现BaseLdapNameAware 接口。第二,需要在application context中定义BaseLdapPathBeanPostProcessor。
实现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();
}
...
}
*在ApplicationContext中指定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 默认会从定义在ApplicationContext中的单实例BaseLdapPathSource (AbstractContextSource)中获取base Path。如果有不止一个BaseLdapPathSource 被定义,那你需要指定baseLdapPathSourceName 属性来使用特定的BaseLdapPathSource 。
7. Spring LDAP Repositories
7.1. 前言
Spring LDAP内置了对Spring Data repositories的支持。基本的功能与配置在 这里。当你使用该功能时,注意一下几点:
- Spring LDAP repositories可以在你的XML配置文件中使用ldap:repositories标签来配置,也可以使用@EnableLdapRepositories注解一个配置类达到相同的目的。
- 为了支持将LdapQuery 的参数自动转换成对应的repositories资源,你需要实现LdapRepository 接口而不是CrudRepository接口。
- 所有的Spring LDAP repositories都要与被注解的entry类配合使用,详情参阅 Object-Directory Mapping (ODM)
- 因为所有ODM管理的类都有DN作为ID。因此所有的Spring LDAP repositories必须有一个类型为javax.naming.Name的ID参数。事实上,内置的LdapRepository 只使用一个参数,那就是托管的实体类(被ODM注解的类),默认为javax.naming.Name类型的ID。
- 由于LDAP协议的原因,Spring LDAP repositories不支持分页与排序。
7.2. QueryDSL 支持
Spring LDAP包含了基本的QueryDSL 的支持。该支持包含以下部分:
- LdapAnnotationProcessor,一个注解处理器,用于在Spring LDAP ODM 注解的基础上生成QueryDSL 。更多ODM注解的信息请参考 Object-Directory Mapping (ODM)。
- QueryDslLdapQuery,一个查询实现类,用于构建与执行QueryDSL 的查询。
- Spring Data repository支持QueryDSL 断言。QueryDslPredicateExecutor 包括一些额外的方法。继承QueryDslPredicateExecutor接口 与LdapRepository 来将QueryDSL 断言支持包括到你的repository中。
8. 连接池支持
8.1. 前言
LDAP 连接池帮助减少每次LDAP交互式都要创建LDAP 连接的开销。而Java LDAP pooling support在配置选项与特性上存在自身的限制,如连接验证与连接池的维护。Spring LDAP在每个ContextSource 基础上都提供了更详细的配置项。
在application context 中的<ldap:context-source />元素下的<ldap:pooling />子元素可以对连接池进行配置。
译者注:下面的句子标点符号混乱,无法正确理解与翻译,原文如下:
Read-only and read-write DirContext objects are pooled separately (if anonymous-read-only is specified.Jakarta Commons-Pool is used to provide the underlying pool implementation.
没错,就是有个单括号。
8.2. DirContext 验证
比起JDK提供的LDAP连接池功能,使用自定义连接池技术的关键动机就是对池连接的验证。验证机制能够保证无论是把DirContext 从连接池取出还是放入连接池甚至是在连接池中处于空闲状态,DirContext 连接都会被检查并确保仍然保持连接与良好的配置。
当连接验证被正确配置后,池中共用的连接都会使用DefaultDirContextValidator来进行验证。DefaultDirContextValidator 会执行DirContext.search(String, String, SearchControls)操作,其中第一个参数为空,第二个参数为objectclass=*的过滤器,第三个参数为SearchControls ,该参数被设置为返回objectClass属性并且超时设置为500ms。如果返回了NamingEnumeration ,则证明DirContext 通过了验证,反之如果没有返回值或者抛出异常,则证明DirContext 没有通过验证。对于大多数的LDAP服务器来说只需要使用默认配置即可,无需任何修改即可正常工作并提供最快的方式来验证DirContext 。如果需要自定义,下面的配置属性可以进行自定义配置。
8.3. 连接池的配置
翻译略 表格
8.4. Pool2 配置
翻译略 表格
8.5. 配置
翻译略 xml文件
8.5.1. 对验证操作的配置
翻译略 xml文件 可以看出就是在取出与空闲时都要对池中的连接对象进行验证。
8.6. 已知问题
8.6.1. 自定义认证
PoolingContextSource 会假定由ContextSource.getReadOnlyContext()获取的所有DirContext 对象都具有相同的环境,同样的,所有由ContextSource.getReadWriteContext()获取的DirContext 也是具有相同的环境。这就意味着在PoolingContextSource 中使用AuthenticationSource 将LdapContextSource 的配置包裹起来可能并不会产生我们预期的功能。(译者注:在上面的情况下,我们预期的是每次验证都从AuthenticationSource 中取认证,而无需配置username 与password了)。这样连接池每次都只会使用第一个用户的凭据,除非使用新的连接,否则,之后的上下文环境中的所有请求在该请求线程中,都不会被AuthenticationSource 中指定的用户来填充(验证)。
9. 添加API中没有的重载方法
9.1. 自定义search方法的实现
翻译略 没什么好说的,大意是虽然在API中有很多重载的方法能够保证大家不同的要求,但是还是会有一些特定的要求需要自定义一个方法,这里它提供了一个接口---SearchExecutor。然后又提供了一个CollectingNameClassPairCallbackHandler类,来处理自定义search后返回的数据,最后将这两个作为参数传入 ldapTemplate.search(executor, handler)方法中。
9.2. 实现其他自定义的Context方法
翻译略 类似的有一个ContextExecutor 接口,实现其方法executeWithContext即可。该方法可以对DirContext做一些自定义的操作,然后通过ldapTemplate.executeReadOnly(executor)来或ldapTemplate.executeReadWrite(executor)调用。
10. 处理DirContext
10.1. 自定义DirContext处理前后的操作
翻译略 这个接口DirContextProcessor实现其方法以便在search操作执行前或执行后做相应的操作。
10.2. 实现一个 Request Control DirContextProcessor
翻译略
10.3. 搜索结果分页
一些查询会返回大量的数据。如果很难通过过滤器将其拆分成更小的数据块,那我们就希望每次查询时只返回一部分数据。这就是我们常说的分页查询结果。每一页的结果都能被展示出来并且能够链接到上一页与下一页。如果没有这个功能的话,客户端只能手动的限制返回到页面的结果,或者获取所有的结果并把它分成合适的数量展示到页面中。前者相当复杂,而后者会消耗不必要的内存空间。
某些LDAPserver提供了对PagedResultsControl的支持,PagedResultsControl要求LDAP服务器通过search操作返回的结果是按特定大小分页的。用户通过search方法中的参数简单的控制返回哪一页数据。但是用户必须跟踪两次方法调用之间的cookie。服务器使用这个cookie来跟踪上次分页请求时返回结果后的位置,以便从该位置继续搜索返回下一页的结果。
Spring LDAP是通过改变上文中讨论过的LdapContext的pre- and postprocessing预处理与后处理方法来实现分页结果支持的。它使用PagedResultsDirContextProcessor来进行处理。PagedResultsDirContextProcessor 根据请求的页的参数创建一个PagedResultsControl对象,并将其加入到LdapContext中。查询之后会得到PagedResultsResponseControl 并获取分页的cookie,该cookie能够将context保持在连续的分页请求之间。
下面的例子展示了如何使用PagedResultsDirContextProcessor的分页查询功能:
使用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来完成。