利用Docker部署管理LDAP及其初次使用

前言:
本周主要写了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并没有想象中复杂,主要是见到了这种树形存储的形式,实际上手操作后会感觉清晰很多。

411 声望
14 粉丝
0 条评论
推荐阅读
如何跑起现有android+springboot+mysql项目
所选android项目版本:29安装对应版本的android sdk安装JDK参考文章: [链接]安装Maven——用于安装后台对应依赖参考文章: [链接]配置nginx——用于连接前后台参考文章: [链接]配置完后:引入项目中的nginx文件重启...

李明阅读 414

angular 中ActivatedRoute 和 Router,以及记录后台遇到的问题
ActivatedRoute 和 Router的区别前台angular使用这两个来进行路由的操作,但是好像一直不大清楚区别。这里简单记录一下。区别 {代码...} ActivatedRoute是当前组件的路由对象,包含了当前的路由信息。router是全...

weiweiyi6阅读 1.3k

记录java 在遍历中删除元素 以及 mysql5.6版本添加unique失败
遍历中删除List或Queue等数据结构中,如何一边遍历一遍删除?1. 常犯错误ArrayList可能没遇到坑过的人会用增强for循环这么写: {代码...} 但是一运行,结果却抛 java.util.ConcurrentModificationException 异常即...

weiweiyi6阅读 975

张晋涛:我的 2022 总结
大家好,我是张晋涛。2022 年已经结束,我每年都会惯例的做个小回顾,今年因为阳了在恢复身体,一直拖到了今天才写。生活在 2022 年初做回顾的时候,觉得 2021 是魔幻的一年,但现在看来 2022 年其实更加魔幻。一...

张晋涛6阅读 1.1k评论 2

封面图
使用springboot+angular实现web端微信扫码登陆
现在微信的使用用户越来越多,如果网站添加上微信登录,就能节省很多用户注册时间,极大缩小了注册流程。会让用户觉得特别方便。接下来我们就说一下怎么来实现Web端微信扫码登录。

郝泽龙_HZ6阅读 966

Docker学习:Image的本地存储结构
在使用Docker时候,针对镜像的操作一般就是docker pull,docker build,docker commit(刚开始接触Docker的时候,还不会Dockerfile,经常使用这个命令,但是经历了一次血的教训,已经放弃这个命令很久)这些操作...

backbp4阅读 10.1k评论 3

使用docker快速搭建xssPlatform测试平台实践
笔者之前给一些开发团队多次做Web安全开发培训,为了让培训的学员能够理解XSS原理和XSS的危害,将xssPlatform进行了更新,之前一直放在GitHub中;发现关注的人越来越多,很多人在安装的过程中遇到问题不知道怎么...

汤青松1阅读 26.3k

411 声望
14 粉丝
宣传栏