LDAP简单介绍及使用

前言

随着企业内部各种开源平台越来越多,例如:gitlab、Jenkins、JumpServer、Rancher等,账号维护变成一件繁琐的事情,这时需要一个统一账号维护的平台,每人只需一个账号,在公司内部平台通用。而大多数开源平台都支持 LDAP,因此只要搭建好 LDAP 服务,将企业内部这些平台都对接到 LDAP,即可实现统一账号管理。

LDAP(Light Directory Access Portocol),它是基于 X.500 标准的轻量级目录访问协议。目录数据库是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。

LDAP目录服务是由目录数据库和一套访问协议组成的系统。

基本概念

  1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。
  2. 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。
  3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。
  4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。
关键字 英文全称 含义
dc Domain Component 域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置)
uid User Id 用户ID songtao.xu(一条记录的ID)
ou Organization Unit 组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如“oa组”(一条记录的所属组织)
cn Common Name 公共名称,如“Thomas Johansson”(一条记录的名称)
sn Surname 姓,如“许”
dn Distinguished Name “uid=songtao.xu,ou=oa组,dc=example,dc=com”,一条记录的位置(唯一)
rdn Relative dn 相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson”

安装服务器

这里我们使用 docker 来部署 openldap

docker pull osixia/openldap
docker run \
    -d \
    -p 389:389 \
    -p 636:636 \
    --env LDAP_ORGANISATION="imooc" \
    --env LDAP_DOMAIN="imooc.com" \
    --env LDAP_ADMIN_PASSWORD="test123456" \
    --name openldap \
    osixia/openldap
  • -p 389:389 TCP/IP访问端口,-p 636:636 SSL连接端口。
  • –name 自行设置容器名称
  • –env LDAP_ORGANISATION 配置LDAP组织名称
  • –env LDAP_DOMAIN 配置LDAP域名
  • –env LDAP_ADMIN_PASSWORD 配置LDAP密码

注意:要开通防火墙的 389 和 636 端口

安装可视化工具:PHPLdapAdmin客户端

docker pull osixia/phpldapadmin
docker run \
    -d \
    -p 8080:80 \
    --name ldap-admin \
    --env PHPLDAPADMIN_HTTPS=false \
    --env PHPLDAPADMIN_LDAP_HOSTS=your_ip \
    osixia/phpldapadmin
  • –env PHPLDAPADMIN_HTTPS=false 禁用HTTPS
  • –env PHPLDAPADMIN_LDAP_HOSTS 配置openLDAP的IP或者域名

注意:要开通防火墙的 8080 端口

在 LDAP 中创建用户

登录

浏览器访问 http://ip:8080

  • 账号为 CN=admin,DC=imooc,DC=com
  • 密码为 test123456

创建 ou

创建用户

dn: uid=xiaoming,ou=beijing,dc=imooc,dc=com
ou: beijing
uid: xiaoming
sn: 小明
cn: xiaoming
givenName: xiaoming
displayName: xiaoming
mail: xiaoming@163.com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
userpassword: 123456

最终目录结构为

java客户端调用

根据 cn 从 beijing 这个组织下查询用户信息

使用jdk内置实现

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import java.util.Hashtable;

public class TestLdap {
    public static void main(String[] args) throws NamingException {
        DirContext dirContext = ldapContext();
        NamingEnumeration<SearchResult> enumeration = dirContext.search("ou=beijing,dc=imooc,dc=com", "cn=xiaoming", getDefaultSearchControls());
        while (enumeration.hasMoreElements()) {
            SearchResult searchResult = enumeration.next();
            System.out.println(searchResult);
        }
    }

    private static DirContext ldapContext() throws NamingException {
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://ip:389");
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=imooc,dc=com");
        env.put(Context.SECURITY_CREDENTIALS, "test123456");
        return new InitialLdapContext(env, null);
    }

    private static SearchControls getDefaultSearchControls() {
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return controls;
    }
}

使用unboundid这个ldapsdk

添加依赖

<dependency>
    <groupId>com.unboundid</groupId>
    <artifactId>unboundid-ldapsdk</artifactId>
    <version>6.0.5</version>
</dependency>
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;

import java.util.List;

public class TestLdap2 {
    public static void main(String[] args) throws LDAPException {
        LDAPConnection ldapConnection = new LDAPConnection();
        ldapConnection.connect("ip", 389);
        ldapConnection.bind("cn=admin,dc=imooc,dc=com","test123456");
        SearchRequest searchRequest = new SearchRequest("ou=beijing,dc=imooc,dc=com", SearchScope.SUB,"cn=xiaoming");
        SearchResult searchResult = ldapConnection.search(searchRequest);
        List<SearchResultEntry> searchEntries = searchResult.getSearchEntries();
        System.out.println(searchEntries);
    }
}

使用spring封装

添加依赖

<dependency>
    <groupId>org.springframework.ldap</groupId>
    <artifactId>spring-ldap-core</artifactId>
    <version>2.4.0</version>
</dependency>
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestSpringLdap {
    public static void main(String[] args) {
        LdapTemplate ldapTemplate = ldapTemplate();
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("cn", "xiaoming"));
        List<Map<String, Object>> searchResult = ldapTemplate.search("ou=beijing,dc=imooc,dc=com", filter.encode(), new LdapUserAttributeMapper());
        System.out.println(searchResult);
    }

    private static LdapTemplate ldapTemplate() {
        LdapContextSource ldapContextSource = new LdapContextSource();
        ldapContextSource.setUrl("ldap://ip:389");
        ldapContextSource.setUserDn("cn=admin,dc=imooc,dc=com");
        ldapContextSource.setPassword("test123456");
        ldapContextSource.afterPropertiesSet();
        return new LdapTemplate(ldapContextSource);
    }

    public static class LdapUserAttributeMapper implements AttributesMapper<Map<String, Object>> {

        @Override
        public Map<String, Object> mapFromAttributes(Attributes attributes) throws NamingException {
            Map<String, Object> map = new HashMap<>();
            NamingEnumeration<? extends Attribute> enumeration = attributes.getAll();
            while (enumeration.hasMoreElements()) {
                Attribute attribute = enumeration.next();
                map.put(attribute.getID(), attribute.get());
            }
            return map;
        }
    }
}

底层就是对 jdk 内置实现的封装。

参考

LDAP统一认证服务解决方案[通俗易懂]
LDAP概念和原理介绍
docker安装openldap
docker部署 OpenLDAP,实现账号的统一管理

posted @ 2024-05-01 21:48  strongmore  阅读(3150)  评论(0编辑  收藏  举报