官网:http://www.springframework.org/ldap
官方文档及例子(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/
JAVA文档(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/
GitHub(大量例子):https://github.com/spring-projects/spring-ldap
Spring LDAP Reference
2.基本使用
2.1 使用AttributesMapper进行search和lookup
(1)通过search返回一个属性值
1 import static org.springframework.ldap.query.LdapQueryBuilder.query; 2 3 public class PersonRepoImpl implements PersonRepo{ 4 private LdapTemplate ldapTemplate; 5 6 public void setLdapTemplate(LdapTemplate ldapTemplate){ 7 this.ldapTemplate = ldapTemplate; 8 } 9 10 public List<String> getAllPersonNames(){ 11 return ldapTemplate.search({ 12 query().where("objectclass").is("person"), 13 new AttributeMapper<String>(){ 14 public String mapFromAttributes(Attribute attrs)throws NamingException{ 15 return (String) attrs.get("cn").get(); 16 } 17 } 18 } 19 }); 20 } 21 }
(2)通过search返回一个Person对象
1 package com.example.repo; 2 import static org.springframework.ldap.query.LdapQueryBuilder.query; 3 4 public class PersonRepoImpl implements PersonRepo { 5 private LdapTemplate ldapTemplate; 6 ... 7 private class PersonAttributesMapper implements AttributesMapper<Person> { 8 public Person mapFromAttributes(Attributes attrs) throws NamingException { 9 Person person = new Person(); 10 person.setFullName((String)attrs.get("cn").get()); 11 person.setLastName((String)attrs.get("sn").get()); 12 person.setDescription((String)attrs.get("description").get()); 13 return person; 14 } 15 } 16 17 public List<Person> getAllPersons() { 18 return ldapTemplate.search(query() 19 .where("objectclass").is("person"), new PersonAttributesMapper()); 20 } 21 }
(3)通过lookup返回一个Person对象
在ldap中,有两个"查询"概念,search和lookup。search是ldaptemplate对每一个entry进行查询,lookup是通过DN直接找到某个条目。
"Entries in LDAP are uniquely identified by their distinguished name (DN). If you have the DN of an entry, you can retrieve(找回) the entry directly without searching for it. This is called a lookup in Java LDAP."
在下面的lookup代码中,ldap会跳过为AttributesMapper查找属性。
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 ... 6 public Person findPerson(String dn) { 7 return ldapTemplate.lookup(dn, new PersonAttributesMapper()); 8 } 9 }
2.2 创建LDAP Queries
ldap的search 包含许多参数,比如:
1 Base LDAP path 基本路径(search应该从LDAP树的哪里开始) 2 Search scope 查询范围(search应该进行到LDAP树的哪一层) 3 returned attributes要返回的属性 4 Search filter 查询过滤器
1 package com.example.repo; 2 import static org.springframework.ldap.query.LdapQueryBuilder.query; 3 4 public class PersonRepoImpl implements PersonRepo { 5 private LdapTemplate ldapTemplate; 6 ... 7 public List<String> getPersonNamesByLastName(String lastName) { 8 9 LdapQuery query = query() 10 .base("dc=261consulting,dc=com") 11 .attributes("cn", "sn") //返回的属性 12 .where("objectclass").is("person") 13 .and("sn").is(lastName); 14 15 return ldapTemplate.search(query, new AttributesMapper<String>() { 17 public String mapFromAttributes(Attributes attrs)throws NamingException { 20 return attrs.get("cn").get(); //查询每一个entry的"cn"值 21 } 22 }); 23 } 24 }
2.3 动态创建 Distinguished Names(DN)
为了简化对DN的使用,spring-ldap提供了LdapNameBuilder,和工具类LdapUtils。
假设一个Person有如下的属性:
1 Attribute Name Attribute Value 2 country Sweden 3 company Some Company 4 fullname Some Person
(1)使用 LdapNameBuilder 动态创建 LdapName
1 package com.example.repo; 2 import org.springframework.ldap.support.LdapNameBuilder; 3 import javax.naming.Name; 4 5 public class PersonRepoImpl implements PersonRepo { 6 public static final String BASE_DN = "dc=example,dc=com"; 7 8 protected Name buildDn(Person p) { 9 return LdapNameBuilder.newInstance(BASE_DN) 10 .add("c", p.getCountry()) 11 .add("ou", p.getCompany()) 12 .add("cn", p.getFullname()) 13 .build(); 14 } 15 ...
(2)用 LdapUtils 获取属性值
1 package com.example.repo; 2 import org.springframework.ldap.support.LdapNameBuilder; 3 import javax.naming.Name; 4 public class PersonRepoImpl implements PersonRepo { 5 ... 6 protected Person buildPerson(Name dn, Attributes attrs) { 7 Person person = new Person(); 8 person.setCountry(LdapUtils.getStringValue(dn, "c")); 9 person.setCompany(LdapUtils.getStringValue(dn, "ou")); 10 person.setFullname(LdapUtils.getStringValue(dn, "cn")); 11 // Populate rest of person object using attributes. 12 13 return person; 14 }
2.4 绑定和解绑
在Ldap中,新增与删除叫做绑定和解绑。
2.4.1 新增数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 ... 6 public void create(Person p) { 7 Name dn = buildDn(p); 8 ldapTemplate.bind(dn, null, buildAttributes(p)); 9 } 10 11 private Attributes buildAttributes(Person p) { 12 Attributes attrs = new BasicAttributes(); 13 BasicAttribute ocattr = new BasicAttribute("objectclass"); 14 ocattr.add("top"); 15 ocattr.add("person"); 16 attrs.put(ocattr); 17 attrs.put("cn", "Some Person"); 18 attrs.put("sn", "Person"); 19 return attrs; 20 } 21 }
2.4.2 删除数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 ... 6 public void delete(Person p) { 7 Name dn = buildDn(p); 8 ldapTemplate.unbind(dn); 9 } 10 }
2.4.3 更新数据
(1)使用 rebind 更新数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 ... 6 public void update(Person p) { 7 Name dn = buildDn(p); 8 ldapTemplate.rebind(dn, null, buildAttributes(p)); 9 } 10 }
(2)使用 modifyAttributes 更新数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 ... 6 public void updateDescription(Person p) { 7 Name dn = buildDn(p); 8 Attribute attr = new BasicAttribute("description", p.getDescription()) 9 ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); 10 ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item}); 11 } 12 }
3.简化 Attribute 的获取和 DirContextAdapter 的操作
3.1 介绍
Java LDAP API 可以注册一个DirContextAdapter来自动创建对象。spring-ldap使用了这个特点,在search和lookup中返回DirContextAdapter实例。
3.2 通过ContextMapper来search 和lookup
任何时候,想要在LDAP数据树中查找entry,spring-ldap都会使用这个entry的DN和Attributes来构建一个DirContextAdapter,这使得我们不再需要使用 AttributesMapper,而是使用ContextMapper来对获取的属性值进行转换。
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 ... 5 private static class PersonContextMapper implements ContextMapper { 6 public Object mapFromContext(Object ctx) { 7 DirContextAdapter context = (DirContextAdapter)ctx; 8 Person p = new Person(); 9 p.setFullName(context.getStringAttribute("cn")); 10 p.setLastName(context.getStringAttribute("sn")); 11 p.setDescription(context.getStringAttribute("description")); 12 return p; 13 } 14 } 15 16 public Person findByPrimaryKey(String name, String company, String country) { 18 Name dn = buildDn(name, company, country); 19 return ldapTemplate.lookup(dn, new PersonContextMapper()); 20 } 21 }
这里特别方便的一点是:当属性具有多值时,可以通过getStringAttributes()来获取。
1 private static class PersonContextMapper implements ContextMapper { 2 public Object mapFromContext(Object ctx) { 3 DirContextAdapter context = (DirContextAdapter)ctx; 4 Person p = new Person(); 5 p.setFullName(context.getStringAttribute("cn")); 6 p.setLastName(context.getStringAttribute("sn")); 7 p.setDescription(context.getStringAttribute("description")); 8 // The roleNames property of Person is an String array 9 p.setRoleNames(context.getStringAttributes("roleNames")); 10 return p; 11 } 12 }
3.2.1 AbstactContextMapper
spring-ldap提供了一个ContextMapper的抽象的基础实现类:AbstractContextMapper。自定义的的PersonContextMapper可以这样写:
1 private static class PersonContextMapper extends AbstractContextMapper { 2 public Object doMapFromContext(DirContextOperations ctx) { //ctx没有用到?? 3 Person p = new Person(); 4 p.setFullName(context.getStringAttribute("cn")); 5 p.setLastName(context.getStringAttribute("sn")); 6 p.setDescription(context.getStringAttribute("description")); 7 return p; 8 } 9 }
3.3 使用DirContextAdapter新增和更新数据
注意新增的时候用的是:DirContextAdapter。更新的时候用的是:DirContextOperations。二者的关系:DirContextAdapter实现了DirContextOperations接口。
3.3.1 新增数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 ... 5 public void create(Person p) { 6 Name dn = buildDn(p); 7 DirContextAdapter context = new DirContextAdapter(dn); 8 //和获取一样,set也可以有多值 9 context.setAttributeValues("objectclass", new String[] {"top", "person"}); 10 context.setAttributeValue("cn", p.getFullname()); 11 context.setAttributeValue("sn", p.getLastname()); 12 context.setAttributeValue("description", p.getDescription()); 13 14 ldapTemplate.bind(context); 15 } 16 }
3.3.2 更新数据
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 ... 5 public void update(Person p) { 6 Name dn = buildDn(p); 7 DirContextOperations context = ldapTemplate.lookupContext(dn); 8 9 context.setAttributeValue("cn", p.getFullname()); 10 context.setAttributeValue("sn", p.getLastname()); 11 context.setAttributeValue("description", p.getDescription()); 12 13 ldapTemplate.modifyAttributes(context); 14 } 15 }
3.3.3 合并新增和更新数据的代码
从前面两段代码可知,新增和更新有重复的代码,因此合并重复代码,整理如下:
1 package com.example.repo; 2 3 public class PersonRepoImpl implements PersonRepo { 4 private LdapTemplate ldapTemplate; 5 6 ... 7 public void create(Person p) { 8 Name dn = buildDn(p); 9 DirContextAdapter context = new DirContextAdapter(dn); 11 context.setAttributeValues("objectclass", new String[] {"top", "person"}); 12 mapToContext(p, context); 13 ldapTemplate.bind(context); 14 } 15 16 public void update(Person p) { 17 Name dn = buildDn(p); 18 DirContextOperations context = ldapTemplate.lookupContext(dn); 19 mapToContext(person, context); 20 ldapTemplate.modifyAttributes(context); 21 } 22 23 protected void mapToContext (Person p, DirContextOperations context) { 24 context.setAttributeValue("cn", p.getFullName()); 25 context.setAttributeValue("sn", p.getLastName()); 26 context.setAttributeValue("description", p.getDescription()); 27 } 28 }
3.4 DirContextAdapter和作为属性值的DN
When managing security groups in LDAP it is very common to have attribute values that represent distinguished names. Since distinguished name equality differs from String equality (例如,空格和大小写在DN的判等中是无视的), calculating attribute modifications using string equality will not work as expected.
假设一个member属性值为:cn=John Doe,ou=People。如果代码写作如下,会被认为是两个值,实际上它代表了同一个DN。
1 ctx.addAttributeValue("member", "CN=John Doe, OU=People")
要写作如下:
1 ctx.addAttributeValue("member", LdapUtils.newLdapName("CN=John Doe, OU=People"))
使用DirContextAdapter来修改group membership:
1 public class GroupRepo implements BaseLdapNameAware { 2 private LdapTemplate ldapTemplate; 3 private LdapName baseLdapPath; 4 5 public void setLdapTemplate(LdapTemplate ldapTemplate) { 6 this.ldapTemplate = ldapTemplate; 7 } 8 9 public void setBaseLdapPath(LdapName baseLdapPath) { 10 this.setBaseLdapPath(baseLdapPath); 11 } 12 13 public void addMemberToGroup(String groupName, Person p) { 14 Name groupDn = buildGroupDn(groupName); 15 Name userDn = buildPersonDn(person.getFullname(),person.getCompany(), person.getCountry()); 19 20 DirContextOperation ctx = ldapTemplate.lookupContext(groupDn); 21 ctx.addAttributeValue("member", userDn); 22 23 ldapTemplate.update(ctx); 24 } 25 26 public void removeMemberFromGroup(String groupName, Person p) { 27 Name groupDn = buildGroupDn(String groupName); 28 Name userDn = buildPersonDn(person.getFullname(),person.getCompany(),person.getCountry()); 32 33 DirContextOperation ctx = ldapTemplate.lookupContext(groupDn); 34 ctx.removeAttributeValue("member", userDn); 35 36 ldapTemplate.update(ctx); 37 } 38 39 private Name buildGroupDn(String groupName) { 40 return LdapNameBuilder.newInstance("ou=Groups").add("cn", groupName).build(); 42 } 43 44 private Name buildPersonDn(String fullname, String company, String country) { 45 return LdapNameBuilder.newInstance(baseLdapPath).add("c", country).add("ou", company).add("cn", fullname).build(); 50 } 51 }
3.5 使用spring-ldap和DirContextAdapter的完整代码
1 package com.example.repo; 2 import java.util.List; 3 4 import javax.naming.Name; 5 import javax.naming.NamingException; 6 import javax.naming.directory.Attributes; 7 import javax.naming.ldap.LdapName; 8 9 import org.springframework.ldap.core.AttributesMapper; 10 import org.springframework.ldap.core.ContextMapper; 11 import org.springframework.ldap.core.LdapTemplate; 12 import org.springframework.ldap.core.DirContextAdapter; 13 import org.springframework.ldap.filter.AndFilter; 14 import org.springframework.ldap.filter.EqualsFilter; 15 import org.springframework.ldap.filter.WhitespaceWildcardsFilter; 16 17 import static org.springframework.ldap.query.LdapQueryBuilder.query; 18 19 public class PersonRepoImpl implements PersonRepo { 20 private LdapTemplate ldapTemplate; 21 22 public void setLdapTemplate(LdapTemplate ldapTemplate) { 23 this.ldapTemplate = ldapTemplate; 24 } 25 26 public void create(Person person) { 27 DirContextAdapter context = new DirContextAdapter(buildDn(person)); 28 mapToContext(person, context); 29 ldapTemplate.bind(context); 30 } 31 32 public void update(Person person) { 33 Name dn = buildDn(person); 34 DirContextOperations context = ldapTemplate.lookupContext(dn); 35 mapToContext(person, context); 36 ldapTemplate.modifyAttributes(context); 37 } 38 39 public void delete(Person person) { 40 ldapTemplate.unbind(buildDn(person)); 41 } 42 43 public Person findByPrimaryKey(String name, String company, String country) { 44 Name dn = buildDn(name, company, country); 45 return ldapTemplate.lookup(dn, getContextMapper()); 46 } 47 48 public List findByName(String name) { 49 LdapQuery query = query() 50 .where("objectclass").is("person") 51 .and("cn").whitespaceWildcardsLike("name"); 52 53 return ldapTemplate.search(query, getContextMapper()); 54 } 55 56 public List findAll() { 57 EqualsFilter filter = new EqualsFilter("objectclass", "person"); 58 return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper()); 59 } 60 61 protected ContextMapper getContextMapper() { 62 return new PersonContextMapper(); 63 } 64 65 protected Name buildDn(Person person) { 66 return buildDn(person.getFullname(), person.getCompany(), person.getCountry()); 67 } 68 69 protected Name buildDn(String fullname, String company, String country) { 70 return LdapNameBuilder.newInstance() 71 .add("c", country) 72 .add("ou", company) 73 .add("cn", fullname) 74 .build(); 75 } 76 77 protected void mapToContext(Person person, DirContextOperations context) { 78 context.setAttributeValues("objectclass", new String[] {"top", "person"}); 79 context.setAttributeValue("cn", person.getFullName()); 80 context.setAttributeValue("sn", person.getLastName()); 81 context.setAttributeValue("description", person.getDescription()); 82 } 83 84 private static class PersonContextMapper extends AbstractContextMapper<Person> { 85 public Person doMapFromContext(DirContextOperations context) { 86 Person person = new Person(); 87 person.setFullName(context.getStringAttribute("cn")); 88 person.setLastName(context.getStringAttribute("sn")); 89 person.setDescription(context.getStringAttribute("description")); 90 return person; 91 } 92 } 93 }
4. ODM(Object-Directory Mapping)
4.1 介绍
对象-关系映射框架比如Hibernate和JPA,都可以使用注解来将数据库的表关系映射成java的对象。spring-ldap也提供类似的功能。
LdapPerations里有这些方法:
1 <T> T findByDn(Name dn, Class<T> clazz) 2 <T> T findOne(LdapQuery query, Class<T> clazz) 3 <T> List<T> find(LdapQuery query, Class<T> clazz) 4 <T> List<T> findAll(Class<T> clazz) 5 <T> List<T> findAll(Name base, SearchControls searchControls, Class<T> clazz) 6 <T> List<T> findAll(Name base, Filter filter, SearchControls searchControls, Class<T> clazz) 7 8 void create(Object entry) 9 void update(Object entry) 10 void delete(Object entry)
4.2 注解
每个注解的含义暂时先不详细解释。//todo
1 @Entry (required) 2 @Id (required) 3 @Attribute 4 @DnAttribute 5 @Transient
4.3 执行
1 @Entry(objectClasses = { "person", "top" }, base="ou=someOu") 2 public class Person { 3 @Id 4 private Name dn; 5 6 @Attribute(name="cn") 7 @DnAttribute(value="cn", index=1) 8 private String fullName; 9 10 // No @Attribute annotation means this will be bound to the LDAP attribute with the same value 12 private String description; 13 14 @DnAttribute(value="ou", index=0) 15 @Transient 16 private String company; 17 18 @Transient 19 private String someUnmappedField; 20 // ...more attributes below 21 }
24 public class OdmPersonRepo { 25 @Autowired 26 private LdapTemplate ldapTemplate; 27 28 public Person create(Person person) { 29 ldapTemplate.create(person); 30 return person; 31 } 32 33 public Person findByUid(String uid) { 34 return ldapTemplate.findOne(query().where("uid").is(uid), Person.class); 35 } 36 37 public void update(Person person) { 38 ldapTemplate.update(person); 39 } 40 41 public void delete(Person person) { 42 ldapTemplate.delete(person); 43 } 44 45 public List<Person> findAll() { 46 return ldapTemplate.findAll(Person.class); 47 } 48 49 public List<Person> findByLastName(String lastName) { 50 return ldapTemplate.find(query().where("sn").is(lastName), Person.class); 51 } 52 }
4.4 ODM和作为属性值的DN
ldap中的安全组通常包含多值属性,每一个属性值都是一个user的DN。这些属性值的处理在前面的 3.4 DirContextAdapter和作为属性值的DN 中提到过。ODM同样有简单的处理办法。
1 @Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=groups") 2 public class Group { 3 4 @Id 5 private Name dn; 6 7 @Attribute(name="cn") 8 @DnAttribute("cn") 9 private String name; 10 11 @Attribute(name="uniqueMember") 12 private Set<Name> members; 13 14 public Name getDn() {return dn;} 18 public void setDn(Name dn) {this.dn = dn;} 21 22 public Set<Name> getMembers() {return members;} 26 public void setMembers(Set<Name> members) {this.members = members;} 29 30 public String getName() { return name;} 34 public void setName(String name) {this.name = name;} 37 38 public void addMember(Name member) {members.add(member);} 42 public void removeMember(Name member) {members.remove(member);} 45 }
5.高级LDAP Queries
//todo 暂略。
6.配置
6.1 介绍
采用xml文件方式配置时,需要加入如下xml的命名空间。
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:ldap="http://www.springframework.org/schema/ldap" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/ldap
7 http://www.springframework.org/schema/ldap/spring-ldap.xsd">
6.2 ContextSource 配置
最简单的配置至少需要:username,password,url。完整的属性参看官方文档的说明。
1 <ldap:context-source 2 username="cn=Administrator" 3 password="secret" 4 url="ldap://localhost:389" />
6.2.1 DirContextAuthentication
创建并使用DirContext实例时,通常需要认证这些contexts,有不同的配置方式可供选择。
在这一节里(12. User Authentication using Spring LDAP)讨论了使用spring ldap进行用户验证。
authenticated contexts可以默认创建为read-only和read-write两种。在context-source的配置中指定username和password。如果username是一个LDAP 用户的dn,那么不管在context-source中是否配置了base LDAP path,这个user的DN也必须是username。(没看懂)
有些LDAP服务器支持匿名read-only访问。如果需要支持这个功能,将属性anonymous-read-only设置为true即可。
(1)自定义DirContext的认证过程
spring ldap中的默认认证策略是简单的。它将principal(这里是username)和credentials(这里是password)放在哈希表里传递给DirContext的构造函数。这通常是不够的。因此可以在context-source里配置 authentication-strategy-ref,来指定自定义策略。
(2)使用SpringSecurityAuthenticationSource我们前面的配置,直接将username和password写死了。但是一个更常见的场景是,当为某个用户执行LDAP操作时,应该使用的是当前用户的principals和credentials,而不是显式指定。这可以通过配置SpringSecurityAuthenticationSource实现动态配置。
1 <beans> 2 ... 3 <ldap:context-source 4 url="ldap://localhost:389" 5 authentication-source-ref="springSecurityAuthenticationSource/> 6 7 <bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" /> 8 ... 9 </beans>
6.2.2 本地java LDAP Pooling//todo
6.2.3 高级ContextSource配置//todo
6.3 LdapTemplate Configuration
1 <ldap:ldap-template />
可供配置的属性有:
属性 | 默认值 | 说明 |
id | ldapTemplate | |
context-source-ref | contextSource | 使用的contextSource |
count-limit | 0 | search的默认个数限制,0指的是没有限制。 |
time-limit | 0 | search的默认时间限制,0指的是没有限制。 |
search-scope | SUBTREE | 可选值有:OBJECT,ONELEVEL,SUBTREE |
ignore-name-not-found | false | 指明search时异常NameNotFoundException是否被忽视。 |
ignore-partial-result | false | 指明search时异常PartialResultException是否被忽视。 |
odm-ref | 使用的ObjectDirectoryMapper的bean id。默认是DefaultObjectDirectoryMapper |
6.4 获取base LDAP path的值
通常情况下,在contextSource里定义了base LDAP path,然后操作时都是用的相对路径。但也有某些情况下,需要用到base LDAP path的完整值。比如,操作LDAP groups时,在这里group member的属性值必须是member的完整DN。
step1:实现接口BaseLdapNameAware
1 package com.example.service; 2 public class PersonService implements PersonService, BaseLdapNameAware { 3 ... 4 private LdapName basePath; 5 6 public void setBaseLdapPath(LdapName basePath) { 7 this.basePath = basePath; 8 } 9 ... 10 private LdapName getFullPersonDn(Person person) { 11 return LdapNameBuilder.newInstance(basePath) 12 .add(person.getDn()) 13 .build(); 14 } 15 ... 16 }
step2:配置BaseLdapPathBeanPostProcessor
1 <beans> 2 ... 3 <ldap:context-source 4 username="cn=Administrator" 5 password="secret" 6 url="ldap://localhost:389" 7 base="dc=261consulting,dc=com" /> 8 ... 9 <bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /> 10 </beans>