Spring Boot 2.1.6,Spring Data LDAP 2.1.9
添加maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<!--使用unboundid嵌入式LDAP-->
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
添加ldap-server.ldif,放在resource目录下,用于初始化unboundid嵌入式LDAP
dn: dc=shpun,dc=com
objectClass: top
objectClass: domain
dn: ou=people,dc=shpun,dc=com
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=zhangsan1001uid,ou=people,dc=shpun,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: zhangsan1001cn
sn: zhangsan1001sn
description: zhangsan1001description
title: zhangsan1001title
uid: zhangsan1001uid
displayName: zhangsan1001displayName
dn: uid=zhangsan1002uid,ou=people,dc=shpun,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: zhangsan1002cn
sn: zhangsan1002sn
description: zhangsan1002description
title: zhangsan1002title
uid: zhangsan1002uid
displayName: zhangsan1002displayName
dn: uid=zhangsan1003uid,ou=people,dc=shpun,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: zhangsan1003cn
sn: zhangsan1003sn
description: zhangsan1003description
title: zhangsan1003title
uid: zhangsan1003uid
displayName: zhangsan1003displayName
dn: uid=zhangsan1004uid,ou=people,dc=shpun,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: zhangsan1004cn
sn: zhangsan1004sn
description: zhangsan1004description
title: zhangsan1004title
uid: zhangsan1004uid
displayName: zhangsan1004displayName
dn: uid=zhangsan1005uid,ou=people,dc=shpun,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: zhangsan1005cn
sn: zhangsan1005sn
description: zhangsan1005description
title: zhangsan1005title
uid: zhangsan1005uid
displayName: zhangsan1005displayName
application.yml 添加配置
spring:
ldap:
embedded:
ldif: classpath:ldap-server.ldif
base-dn: dc=shpun,dc=com
Person类
注解 | 解释 |
---|---|
@Entry | 类级别注释,指示objectClass实体映射的定义。(需要) |
@Id | 表示实体DN,声明此属性的字段必须是javax.naming.Name类的派生。(需要) |
@DnAttribute | 表示dn属性到对象类字段的映射。 |
@Attribute | 表示目录属性到对象类字段的映射。 |
@Transient | 表示该字段不是持久的,应该被OdmManager忽略。 |
上述ldif文件中,unboundid需要将@Entry的base属性设置为“ou=people,dc=shpun,dc=com”
@DnAttribute中的属性index能计算的条目中的dn的索引,并自动设值到dn中。因为ldif中定义uid为dn,所以根据上面@Entry的属性base的顺序,index应该为3;也可以用下述方式自己设值,赋值给dn。
public void setUid(String uid) {
this.uid = uid;
this.dn = LdapNameBuilder.newInstance("ou=people,dc=shpun,dc=com")
.add("uid",uid)
.build();
}
@Data
@Entry(base = "ou=people,dc=shpun,dc=com", objectClasses = {"inetOrgPerson","organizationalPerson","person","top"})
public class Person {
@Id
private Name dn;
@DnAttribute(value = "uid",index = 3)
private String uid;
@Attribute(name = "cn")
private String commonName;
@Attribute(name = "sn")
private String surName;
private String description;
private String title;
private String displayName;
}
PersonRepository
public interface PersonRepository extends LdapRepository<Person> {
Person findPersonByUid(String uid);
}
以下增删改查测试均可以,因为是嵌入式LDAP,仅用于测试使用,通过上述ldif文件初始化LDAP,并没有写入到上述的ldif文件中。可以在添加完再打断点进行查询,会发现是有写进去。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UnboundidRepositoryTest {
@Autowired
private PersonRepository personRepository;
@Test
public void findAll(){
Iterable<Person> personIterable = personRepository.findAll();
}
@Test
public void insert(){
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1006cn");
person.setSurName("zhangsan1006sn");
person.setDescription("zhangsan1006description");
person.setTitle("zhangsan1006title");
person.setDisplayName("zhangsan1006displayName");
personRepository.save(person);
findAll();
}
@Test
public void update(){
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1006cn");
person.setSurName("zhangsan1006sn");
person.setDescription("zhangsan1006description");
person.setTitle("zhangsan1006title");
person.setDisplayName("zhangsan1006displayName");
personRepository.save(person);
Person resultPerson = personRepository.findPersonByUid("zhangsan1006uid");
person.setDisplayName("zhangsan1006displayNameUpdate");
personRepository.save(person);
resultPerson = personRepository.findPersonByUid("zhangsan1006uid");
}
@Test
public void delete(){
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1006cn");
person.setSurName("zhangsan1006sn");
person.setDescription("zhangsan1006description");
person.setTitle("zhangsan1006title");
person.setDisplayName("zhangsan1006displayName");
personRepository.save(person);
Person resultPerson = personRepository.findPersonByUid("zhangsan1006uid");
personRepository.delete(person);
findAll();
}
}
因为采用了嵌入式的LDAP服务器,也仅限于我们本地测试开发使用,真实环境下LDAP服务端必然是独立部署的。
可安装LDAP开源实现OpenLDAP OpenLDAP Windows 安装
修改application.yml中的配置
spring:
ldap:
# embedded:
# ldif: classpath:ldap-server.ldif
# base-dn: dc=shpun,dc=com
urls: ldap://127.0.0.1:389
base: dc=shpun,dc=com
username: cn=Manager,dc=shpun,dc=com
password: secret
修改Person类
OpenLDAP 中@Entry的属性base需要配置成“ou=people”,并且 @DnAttribute的属性index改为1
这是两者不同的地方,大坑。之前测试unboundid均可以,但OpenLDAP就不可以,会一直报dn有问题。但查询的时候dn赋值又可以。还有indexOutOf的错误,其实是@DnAttribute的属性index超出了@Entry的属性base设置的位置。但这两个问题在网上我是没有找到。
一开始以为windows版的问题,又装了linux版,问题依然存在。就又去查使用Java API的方式操作OpenLDAP,也碰到这个问题。在Java API中查询dn时意外的去掉“dc=shpun,dc=com”就可以了,然后在返回来把@Entry中的属性base和@DnAttribute的index也尝试修改,就可以了。
@Data
@Entry(base = "ou=people", objectClasses = {"inetOrgPerson","organizationalPerson","person","top"})
public class Person {
@Id
private Name dn;
@DnAttribute(value = "uid",index = 1)
private String uid;
@Attribute(name = "cn")
private String commonName;
@Attribute(name = "sn")
private String surName;
private String description;
private String title;
private String displayName;
}
使用Repository方式测试均可以
@RunWith(SpringRunner.class)
@SpringBootTest
public class OpenLDAPRepositoryTest {
@Autowired
private PersonRepository personRepository;
@Test
public void findPersonByUid(){
Person person = personRepository.findPersonByUid("zhangsan1001uid");
}
@Test
public void findAll(){
Iterable<Person> personIterable = personRepository.findAll();
}
@Test
public void findAllByLdapQuery(){
LdapQuery query = query()
.where("objectclass").is("inetOrgPerson")
.and("sn").is("zhangsan1001sn");
LdapQuery filter = query()
.filter(new EqualsFilter("objectclass","inetOrgPerson"));
Iterable<Person> personIterable = personRepository.findAll(filter);
}
@Test
public void insert(){
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1001cn姓名");
person.setSurName("zhangsan1006sn姓");
person.setDescription("zhangsan1006description描述");
person.setTitle("zhangsan1006title职称");
person.setDisplayName("zhangsan1006displayName名片");
personRepository.save(person);
}
@Test
public void update(){
Person resultPerson = personRepository.findPersonByUid("zhangsan1006uid");
resultPerson.setDisplayName("zhangsan1006displayNameUpdate");
personRepository.save(resultPerson);
}
@Test
public void delete(){
Person resultPerson = personRepository.findPersonByUid("zhangsan1006uid");
personRepository.delete(resultPerson);
}
}
使用LdapTemplate方式,配置获取的也是application.yml,代码参照官网的例子,注意unboundid和OpenLDAP的不同
@RunWith(SpringRunner.class)
@SpringBootTest
public class OpenLDAPTemplateTest {
@Autowired
private LdapTemplate ldapTemplate;
/**
* ldapTemplate 查询 inetOrgPerson cn属性
*/
@Test
public void getAllPersonNames() {
List<String> cnList = ldapTemplate.search(
query().where("objectclass").is("inetOrgPerson"), new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get().toString();
}
});
}
/**
* 返回Person对象的AttributesMapper
*/
private class PersonAttributesMapper implements AttributesMapper<Person> {
public Person mapFromAttributes(Attributes attrs) throws NamingException {
Person person = new Person();
person.setDn(LdapNameBuilder.newInstance("dc=shpun,dc=com").add("ou","people").add("uid",attrs.get("uid").get()).build());
person.setUid((String)attrs.get("uid").get());
person.setCommonName((String)attrs.get("cn").get());
person.setSurName((String)attrs.get("sn").get());
person.setDescription((String)attrs.get("description").get());
person.setTitle((String)attrs.get("title").get());
person.setDisplayName((String)attrs.get("displayName").get());
return person;
}
}
@Test
public void getAllPersons() {
List<Person> personList = ldapTemplate.search(query()
.where("objectclass").is("inetOrgPerson"), new PersonAttributesMapper());
}
/**
* 查找dn Person对象
*
* unboundid 需要 uid=zhangsan1001uid,ou=people,dc=shpun,dc=com
* openldap 需要 uid=zhangsan1001uid,ou=people
*/
@Test
public void findPersonByDn() {
String dn = "uid=zhangsan1001uid,ou=people";
Person person = ldapTemplate.lookup(dn, new PersonAttributesMapper());
}
/**
* LdapQueryBuilder 构建查询
*
* unboundid .base("dc=shpun,dc=com") 有无都可以
* openldap .base("dc=shpun,dc=com") 去掉才可以
*/
@Test
public void getPersonNamesBySn() {
LdapQuery query = query()
//.base("dc=shpun,dc=com")
.attributes("cn")
.where("objectclass").is("inetOrgPerson")
.and("sn").is("zhangsan1001sn");
List<String> cnList = ldapTemplate.search(query, new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs)
throws NamingException {
return String.valueOf(attrs.get("cn").get());
}
});
}
/**
* LdapUtils 获取属性
*/
private Person buildPerson(Name dn, Attributes attrs) {
Person person = new Person();
person.setUid(LdapUtils.getStringValue(dn, "uid"));
person.setCommonName(LdapUtils.getStringValue(dn, "cn"));
person.setSurName(LdapUtils.getStringValue(dn, "sn"));
person.setDescription(LdapUtils.getStringValue(dn, "description"));
person.setTitle(LdapUtils.getStringValue(dn, "title"));
person.setDisplayName(LdapUtils.getStringValue(dn, "displayName"));
return person;
}
/**
* 构建dn
*
* unboundid newInstance("dc=shpun,dc=com") 添加可以
* openldap newInstance("") 去掉可以
*/
private Name buildDn(String uid) {
return LdapNameBuilder.newInstance("")
.add("ou", "people")
.add("uid", uid)
.build();
}
/**
* 构建属性
*/
private Attributes buildAttributes(Person person) {
Attributes attrs = new BasicAttributes();
BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
ocattr.add("organizationalPerson");
ocattr.add("inetOrgPerson");
attrs.put(ocattr);
attrs.put("uid", person.getUid());
if(person.getCommonName() != null && !person.getCommonName().isEmpty()){
attrs.put("cn",person.getCommonName());
}
if(person.getSurName() != null && !person.getSurName().isEmpty()){
attrs.put("sn",person.getSurName());
}
if(person.getDescription() != null && !person.getDescription().isEmpty()){
attrs.put("description",person.getDescription());
}
if(person.getTitle() != null && !person.getTitle().isEmpty()){
attrs.put("title",person.getTitle());
}
if(person.getDisplayName() != null && !person.getDisplayName().isEmpty()){
attrs.put("displayName",person.getDisplayName());
}
return attrs;
}
/**
* 新增person
*/
@Test
public void ldapTemplateBind() {
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1006cn");
person.setSurName("zhangsan1006sn");
person.setDescription("zhangsan1006description");
person.setTitle("zhangsan1006title");
person.setDisplayName("zhangsan1006displayName");
Name dn = buildDn(person.getUid());
ldapTemplate.bind(dn, null, buildAttributes(person));
}
/**
* 更新person
*/
@Test
public void update() {
Person person = new Person();
person.setUid("zhangsan1006uid");
person.setCommonName("zhangsan1006cn");
person.setSurName("zhangsan1006sn");
person.setDescription("zhangsan1006description");
person.setTitle("zhangsan1006title");
person.setDisplayName("zhangsan1006displayNameUpdate");
Name dn = buildDn(person.getUid());
//unbind 删除 bind 新增
ldapTemplate.rebind(dn, null, buildAttributes(person));
}
/**
* 更新person 具体属性
*/
@Test
public void updateSurName() {
Person person = new Person();
person.setUid("zhangsan1006uid");
Name dn = buildDn(person.getUid());
Attribute attr = new BasicAttribute("title", "zhangsan1006titleUpdate");
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
}
/**
* DirContextAdapter 简化查询
*
* unboundid newInstance("dc=shpun,dc=com") 添加可以
* openldap newInstance("") 去掉可以
*/
private static class PersonContextMapper implements ContextMapper {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter)ctx;
Person person = new Person();
person.setDn(LdapNameBuilder.newInstance("dc=shpun,dc=com").add("ou","people").add("uid",context.getStringAttribute("uid")).build());
person.setUid(context.getStringAttribute("uid"));
person.setCommonName(context.getStringAttribute("cn"));
person.setSurName(context.getStringAttribute("sn"));
person.setDescription(context.getStringAttribute("description"));
person.setTitle(context.getStringAttribute("title"));
person.setDisplayName(context.getStringAttribute("displayName"));
//getStringAttributes
return person;
}
}
@Test
public void findByPrimaryKey() {
String uid = "zhangsan20000uid";
Name dn = buildDn(uid);
Person person = (Person)ldapTemplate.lookup(dn, new PersonContextMapper());
}
/**
* 删除person
*/
@Test
public void delete() {
Person person = new Person();
person.setUid("zhangsan1006uid");
Name dn = buildDn(person.getUid());
ldapTemplate.unbind(dn);
}
}
参考:
spring ldap reference
spring ldap docs
Spring Boot中使用LDAP来统一管理用户信息