5

前言:
本周主要写了gitlabWebhook转github的项目,总体上没有遇到什么大问题,这周接触到了LDAP,于是就花时间实际操作了解了一下。

一、什么是LDAP

LDAP是轻量级目录访问协议的简称。
目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。

二、为什么要用LDAP

我们利用LDAP要达到的最终目的就是实现统一登录,比如我们有多个系统,每个系统都是独立运行,但是我们想要为了方便管理想要把这些用户全存在一起并且这些系统登录时统一向这个库进行查询。
那么这时候我们就用到了LDAP。

三、用docker部署LDAP和LDAP可视化界面

1、安装openldap


docker run \
    -d \
    -p 389:389 \
    -p 636:636 \
    -v /usr/local/ldap:/usr/local/ldap \
    -v /data/openldap/ldap:/var/lib/ldap \
    -v /data/openldap/slapd.d:/etc/ldap/slapd.d \
    --env LDAP_ORGANISATION="imysh" \
    --env LDAP_DOMAIN="imysh.com" \
    --env LDAP_ADMIN_PASSWORD="123456" \
    --name openldap \
    --hostname openldap-host\
    --network bridge \
    osixia/openldap

其中 -p 389:389 \ TCP/IP访问端口
-p 636:636 \ SSL连接端口。
–network bridge 连接默认的bridge网络(docker0)
–hostname openldap-host 设置容器主机名称为 openldap-host
–env LDAP_ORGANISATION=“imysh” 配置LDAP组织名称
–env LDAP_DOMAIN=“imysh.com” 配置LDAP域名
–env LDAP_ADMIN_PASSWORD=“123456” 配置LDAP密码
默认登录用户名:admin

2、安装可视化界面phpidapadmin

docker run \
    -p 8081:80 \
    --privileged \
    --name phpldapadmin \
    --env PHPLDAPADMIN_HTTPS=false \
    --env PHPLDAPADMIN_LDAP_HOSTS=192.168.x.xxx  \
    --detach osixia/phpldapadmin

其中 PHPLDAPADMIN_LDAP_HOSTS 为配置openLDAP的IP或域名,我们是在本地起的,所以填写我们本机IP即可。
查询本机IP:
ifconfig | grep "inet"
image.png

存储方式

不像我们熟知的关系数据库, 数据都在表中,一行一行的,一目了然,这个OpenLDAP是以树的方式存储的。 比如一个人的信息是这样的:
image.png
名词解释:
dc:域组织(可以把它当作关系型中的库)

比如将 http://liming.tcom这样的域名,可以拆成 dc=liming,dc=com这样的形式。

ou:指代组织单元、部门
cn:通用名称,一般为用户名
dn:用来唯一标识一项的路径。(一条dn就类似于关系型数据库中的一条数据)
例:
image.png
比如这个张三的dn:cn=zhang san,cn=newgroup,dc=imysh,dc=com
image.png

访问管理首页

访问: 127.0.0.1:8081
image.png
用我们初始化的管理员账号登录
账号:cn=admin,dc=imysh,dc=com
密码:123456

新建部门、组和用户

新建部门(ou):
image.png
image.png

新建组(cn)
image.png

新建用户
image.png
image.png
我们可以在新建用户时配置他的姓氏名称以及所属组、用户名、密码、加密方式等信息。

四、SpringBoot集成LDAP

所用依赖:

<dependency>
            <groupId>com.sun</groupId>
            <artifactId>ldapbp</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>2.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>compile</scope>
        </dependency>

配置:

/**
 * LDAP 的自动配置类
 *
 * 完成连接 及LdapTemplate生成
 */
@Configuration
public class LdapConfiguration {

    private LdapTemplate ldapTemplate;

    @Bean
    public LdapContextSource contextSource() {
        LdapContextSource contextSource = new LdapContextSource();
        Map<String, Object> config = new HashMap();

        contextSource.setUrl(LdapConstans.url);
        contextSource.setBase(LdapConstans.BASE_DC);
        contextSource.setUserDn(LdapConstans.username);
        contextSource.setPassword(LdapConstans.password);

        //  解决 乱码 的关键一句
        config.put("java.naming.ldap.attributes.binary", "objectGUID");

        contextSource.setPooled(true);
        contextSource.setBaseEnvironmentProperties(config);
        return contextSource;
    }
 
    @Bean
    public LdapTemplate ldapTemplate() {
        if (null == ldapTemplate)
            ldapTemplate = new LdapTemplate(contextSource());
        return ldapTemplate;
    }

}


class LdapConstants {
  static String url = "ldap://192.168.31.73";
  static String BASE_DC = "dc=imysh,dc=com";
  static String username = "cn=admin,dc=imysh,dc=com";
  static String password = "123456";
}

通过上面的代码,在IOC容器完成bean的定义,我们在外部就可以注入使用LdapTemplate了

LdapTemplate完成CRUD功能:

建立person类:


@Data
@Entry(base = "cn=group,ou=department", objectClasses="inetOrgPerson")
public class Person {

  /**
   * 此字段不能少
   */
  @Id
  private Name id;

  @DnAttribute(value = "uid", index = 3)
  private String uid;

  @Attribute(name = "cn")
  private String commonName;

  @Attribute(name = "sn")
  private String myUerName;

  private String userPassword;

}

问题:这些对应LDAP的属性(sn、cn)是从哪来的?
image.png

新增person

 @Test
  public void addPerson() {
    Person person = new Person();
    person.setUid("uid:15");
    person.setMyUerName("liMing");
    person.setCommonName("liming");
    person.setUserPassword("123456");
    ldapTemplate.create(person);
  }

结果:
image.png

查询person

@Test
public void getUsers() {
    String filter = "(&(objectclass=inetOrgPerson))";
    List<Person> list = ldapTemplate.search("cn=group,ou=department", filter, new AttributesMapper() {
      @SneakyThrows
      @Override
      public Object mapFromAttributes(Attributes attributes) throws NamingException {
        // 可以通过下面方法打印属性
        NamingEnumeration<? extends Attribute> att = attributes.getAll();
        while (att.hasMore()) {
          Attribute a = att.next();
          System.out.println(a.getID() + "=" + a.get());
        }
        Person user = new Person();

        Attribute a = attributes.get("sn");
        if (a != null) user.setMyUerName((String)a.get());

        a = attributes.get("userPassword");
        if (a != null) user.setUserPassword(a.get().toString());

        a = attributes.get("cn");
        if (a != null) user.setCommonName((String) a.get());
        return user;
      }
    });
    System.out.println(list);
  }

对于其中的filter要注意filter整体要在括号里,否则会报如下错误:

org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'cn=group,ou=department'
  • 获取域列表,如果要获取确定的某一个域,filter可以这样写: (&(objectclass=dcObject)&(dc=imysh))
  • 获取组织列表,如果要获取确定的某一个组织,filter可以这样写: (&(objectclass=organizationalUnit)&(ou=people)
  • 获取people列表,如果要获取某一个确定的人,filter可以这样写: (&(objectclass=inetOrgPerson)&(uid=uid:13))

结果:
image.png
image.png

更新

  @Test
  public void update(){
    String oldPersonDn = " uid=uid:15,cn=group,ou=department";
    Person newPerson = new Person();
    newPerson.setMyUerName("newLiming");
    newPerson.setCommonName("newCommentName");
      ldapTemplate.modifyAttributes(oldPersonDn, new ModificationItem[] {
          new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("cn", newPerson.getCommonName().trim())),
          new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("sn", newPerson.getMyUerName().trim())),
      });
  }

更新就是替换属性,使用ModificationItem类进行处理。
就像我们之前使用的数据库一样,最简单的就是通过ID来进行更新,也就相当于这里的dn
结果:
image.png

删除

@Test
  public void delete() {
    String PersonDn = " uid=uid:15,cn=group,ou=department";
    ldapTemplate.unbind(PersonDn);
  }

结果:
image.png
删除也就是解绑的过程,直接调用unbind即可

总结:LDAP主要是为了进行用户的统一认证,但是由于时间限制并没有测试到这里,但是总体上感觉起来LDAP并没有想象中复杂,主要是见到了这种树形存储的形式,实际上手操作后会感觉清晰很多。


李明
441 声望19 粉丝