Spring Data LDAP 操作 unboundid 和 OpenLDAP

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来统一管理用户信息

发布了57 篇原创文章 · 获赞 11 · 访问量 9879

猜你喜欢

转载自blog.csdn.net/qq_36160730/article/details/97113956