操作手册 : AD 及 LDAP 操作

操作手册 : AD 及 LDAP 操作本文详述了 LDAP 的基础知识 包括其特点 服务器模型和属性

大家好,欢迎来到IT知识分享网。

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 https://juejin.cn/post/
Github : 👉 https://github.com/black-ant
CASE 备份 : 👉 https://gitee.com/antblack/case




操作手册系列主要记录平时积累的无体系的操作笔记 , 这一篇主要是 LDAP (AD) 的相关笔记 .

前言

LDAP 是 一个 轻量目录协议 ,全名 Lightweight Directory Access Protocol , 他用于发布目录信息到许多不同资源的协议,LDAP 类似于一个集中的地址本 ,类似于一个电话簿

而本篇会涉及到的是 OpenLDAP 和 Windows AD , 他们两者都是 LDAP 协议的实现类 .

一 . LDAP 基础

特点 :

  • LDAP 支持TCP / IP
  • LDAP 可以说一个特殊的数据库 , LDAP 实现了数据结构的存储
  • LDAP 采用树状结构
  • LDAP 对查询进行了优化 , 他的读性能更加优秀
  • LDAP是一种开放Internet标准,LDAP协议是跨平台的Interent协议
  • 通过推和拉来复制技术 ,允许使用ACI ( 访问权限的控制 )
  • LDAP 协议是开发的标准协议
  • LDAP支持强认证方式

LDAP 服务器

  • LDAP 采用 Client/server模型
  • LDAP 服务是 由 目录数据库 和 一套访问协议组成的系统
  • LDAP 服务器用来处理查询和更新LDAP 目录

二 . LDAP 属性

关键组成

  • dc : 一条记录所属区域 ( 哪一棵树 ) ,域名组件 ,在最顶层
  • dc -> Domain Component
  • dn (distinguished Name 唯一标识名): 一条记录的详细位置
  • rdn : 类似于相对路劲 : CN=张三
  • rdn -> Relative Distinguished Name
  • Base DN : LDAP目录树的最顶部,即根
  • GUID : 全局唯一标识 ,GUID 是一个 128位 数值
  • UPN : 用户主体名称 ,比DN 更加短的标识路径
  • UPN :

Attribute 默认属性:

  • cn common Name: 姓名
  • sn sur name: 姓
  • ou organizational Unit Name: 单位(部门)名称 ,所属组织
  • o organization: 组织-公司
  • c countryName: 国家
  • dc domainComponent : 域名
  • telephoneNumber : 电话号码
  • objectClass : 内置属性

结构

  • dc -> ou -> cn
  • dc是一个域名 ,一颗数 ,树下有很多 ou 组织 ,组织下拥有 cn
  • n 是路劲 ,例如 :CN=张三,OU=Web前端组,OU=软件开发部,DC=moonxy,DC=com
  • n 相对路劲 : CN=张三 、OU=Web前端组

基本概念

  • Entry : 条目 ,记录项 ,是最基础的记录单元 (dn + rdn + Base DN)
  • Attribute :属性 ,每个条目都有属性 (名称-值)()
  • ObjectClass : 对象类是属性的集合,对象类有三种类型 ,结构类型(Structural)、抽象类型(Abstract)和辅助类型(Auxiliary)
  • Schema : 模式 ,对象类的集合
  • backend & database : backend 用于操作 ,database 用于存储

三 . LDAP 实现之一 Windows AD

Windows AD 简介

Active Directory 域内的 directory database(目录数据库)被用来存储用户账户、计算机账户、打印机和共享文件夹等对象,而提供目录服务的组件就是 Active Directory (活动目录)域服务(Active Directory Domain Service,AD DS)

在AD 域服务中 ,AD 就是一个命名空间 ,利用AD ,我们可以通过对象的名称来找到和这个对象有关的所有信息

image.png

AD 与 LDAP 直观区别

  • AD : Active Directory : AD 是 windows 的一种服务 ,用于存储 Windows 网络中的用户账号 ,组 ,计算机
  • Active Directory = LDAP服务器+LDAP应用(Windows域控) ,AD 可以说是LDPA 的 一种应用实例 , 通过 LDAP 协议 访问AD
  • Active Directory先实现一个LDAP服务器,然后自己先用这个LDAP服务器实现了自己的一个具体应用(域控)
  • 简单点说 ,就是通过 LDAP 协议将 数据写入 AD Server
  • 通过 AD 的 目录结构 来存储账号是合理的

四 . 操作指南

代码是基于 Windows AD 2012 进行实操 , LDAP 需要自行兼容 . 当然 ,以下代码使用的是 javax.naming , 是比较偏底层的操作方式 , 也可以选择 SpringLDAP , 用的也比较舒服.

!!! 更详细的操作可以参考 net.tirasa.connid.bundles.ldap 包 , 很多操作最开始都是从该包学习的

Node 1 : 创建 Connect

Naming 包中通过 LdapContext 对象实现与 LDAP 的连接 , 其中 SSL 使用 ldaps://636 连接 , 非SSL 使用 ldap://389 进行连接

 private static final String LDAP_CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; public static final String CONNECT_TIMEOUT_ENV_PROP = "com.sun.jndi.ldap.connect.timeout"; public static final String READ_TIMEOUT_ENV_PROP = "com.sun.jndi.ldap.read.timeout"; public LdapContext createLdapContext() { 
    final java.util.Hashtable<Object, Object> env = new java.util.Hashtable<Object, Object>(); // 定义 LDAP 工厂类  env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_CTX_FACTORY); // 构建LDAP 访问地址 env.put(Context.PROVIDER_URL, getLdapUrls()); env.put(Context.REFERRAL, "follow"); // 定义超时时间 env.put(CONNECT_TIMEOUT_ENV_PROP,Long.toString(config.getConnectTimeout())); env.put(READ_TIMEOUT_ENV_PROP, Long.toString(config.getReadTimeout())); // 开启SSL / 信任密钥 if (config.isSsl()) { 
    env.put(Context.SECURITY_PROTOCOL, "ssl"); } env.put(LDAP_CTX_SOCKET_FACTORY, TrustAllSocketFactory.class.getName()); env.put(LDAP_BINARY_ATTRIBUTE, SDDL_ATTR + " " + OBJECTGUID + " " + OBJECTSID); // 访问当时 账号 env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "administrator@antblack"); // 密码 env.put(Context.SECURITY_CREDENTIALS, ""); LdapContext context = null; try { 
    context = new InitialLdapContext(env, null); } catch (NamingException e) { 
    e.printStackTrace(); } return context; } private String getLdapUrls() { 
    StringBuilder builder = new StringBuilder(); builder.append("ldap://"); builder.append(config.getHost()); builder.append(':'); builder.append(config.getPort()); for (String failover : LdapUtil.nullAsEmpty(config.getFailover())) { 
    builder.append(' '); builder.append(failover); } return builder.toString(); } 

Node 2 : 属性构建操作

 // Step 1 : 构建属性 ,Naming 包通过 Attribute 管理属性 final BasicAttributes ldapAttrs = new BasicAttributes(true); dapAttrs.put(ldapAttr); // step 3 : 创建对象 Context context = ctx.createSubcontext("OU=研发0219,DC=antblack,DC=com,DC=cn", adAttrs); // 构建属性 public BasicAttributes getAttriutes() { 
    BasicAttributes adAttrs = new BasicAttributes(true); adAttrs.put(getAttribute("description", "test")); BasicAttribute objectClass = new BasicAttribute("objectClass"); for (String ldapClass : orgClass) { 
    objectClass.add(ldapClass); } adAttrs.put(objectClass); return adAttrs; } public BasicAttribute getAttribute(String key, String value) { 
    return new BasicAttribute(key, value); } // ORG private Set<String> orgClass = new TreeSet<>(); orgClass.add("organizationalUnit"); orgClass.add("top") // GROUP orgClass.add("group"); orgClass.add("top"); // Person orgClass.add("organizationalPerson"); orgClass.add("top"); orgClass.add("person"); orgClass.add("user"); 

Node 3 : 构建 GUID

/ * 转换为 GUID */ public static String exchangeGUID(String guid) { return Hex.getEscaped(GUID.getGuidAsByteArray(guid)); } / * entryDN 转换为 UID */ public String entryExchangeGUID(final String entryDN) throws NamingException { return GUID.getGuidAsString((byte[]) getEntryID(entryDN).get()); } // 通常都是取 entryUUID , 但是也可以通过类型定制 ,如下 / * The LDAP attribute to map Uid to. */ private String uidAttribute = "entryUUID"; / * The LDAP attribute to map Gid to. */ private String gidAttribute = "entryUUID"; if (oclass.equals(ObjectClass.GROUP)) { clazz = oclass; idAttribute = conn.getConfiguration().getGidAttribute(); } else if (oclass.equals(ObjectClass.ACCOUNT)) { clazz = oclass; idAttribute = conn.getConfiguration().getUidAttribute(); } else { clazz = ObjectClass.ALL; idAttribute = null; } 

Node 4 : AD 查询

LDAP 有一套完整的查询语句 , 以下举例
@ https://www.ietf.org/rfc/rfc2254.txt

// 查询所有 (objectClass=*) // objectGUID 查询 (&(objectClass=*)(objectGUID=c92131cee-dd23-445-9967-c7)) (objectGUID=l\D7\ABTP\14\0CL\B5\A9\3E\E60\F0\90\29) // GUID 要转换格式 16 进制 // cn 查询 (&(objectClass=*)(cn=O组织U125)) // sAMAccountName (&(objectClass=*)(sAMAccountName=O组织U125)) // syncSearch (|(&(objectClass=user))(objectClass=group)(&(isDeleted=FALSE)(objectClass=user))) // Java 查询方式 public void search(String info, String baseOUName) throws NamingException { 
    // step 1 : 搜索根路径 --> baseDN baseOUName = "OU=" + baseOUName + ",DC=antblack,DC=com,DC=cn"; // step 2 : 定义搜索范围 SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); // step 3 : 准备搜索语句 String searchFilter = ADSearchUtils.getNativeFilter(ADSearchType.EQUALS, "name", info, ObjectClass.ORGANIZATION); // step 4 : 搜索 try { 
    final NamingEnumeration<SearchResult> results = ctx.search(baseOUName, searchFilter, searchCtls); // step 5 : 处理结果 Set backMap = new HashSet(); while (results.hasMoreElements()) { 
    SearchResult sr = (SearchResult) results.next(); logger.info("------> this is result :{} <-------", sr.getAttributes().get("name")); backMap.add(sr.getAttributes()); } logger.info("------> this back is :{} <-------", backMap.size()); } catch (NamingException e) { 
    logger.error("E----> error :{} -- content :{}", e.getClass() + e.getMessage(), e); throw e; } } // 标识符类型 filter = "(" filtercomp ")" filtercomp = and / or / not / item and = "&" filterlist or = "|" filterlist not = "!" filter filterlist = 1*filter item = simple / present / substring / extensible simple = attr filtertype value filtertype = equal / approx / greater / less equal = "=" approx = "~=" greater = ">=" less = "<=" extensible = attr [":dn"] [":" matchingrule] ":=" value / [":dn"] ":" matchingrule ":=" value present = attr "=*" substring = attr "=" [initial] any [final] initial = value any = "*" *(value "*") final = value attr = AttributeDescription from Section 4.1.5 of [1] matchingrule = MatchingRuleId from Section 4.1.9 of [1] value = AttributeValue from Section 4.1.6 of [1] // 查询操作可参考文档 // 注意点 : 1 搜索的时候同样要考虑特殊字符 , 可以使用 ASCII 码替换 , 例如 (cn=*\2a*) (o=Parens R Us \28for all your parenthetical needs\29) (cn=*\2A*) (filename=C:\5cMyFile) (bin=\00\00\00\04) (sn=Lu\c4\8di\c4\87) 

Node 5 : 其他操作

// 修改属性 public final static int ADD_ATTRIBUTE = 1; public final static int REPLACE_ATTRIBUTE = 2; public final static int REMOVE_ATTRIBUTE = 3; modifyAttributes(entryDN, attrToModify, DirContext.REPLACE_ATTRIBUTE); private void modifyAttributes(final String entryDN, final List<ModificationItem> modItems) { 
    try { 
    conn.getInitialContext().modifyAttributes(entryDN, modItems.toArray(new ModificationItem[modItems.size()])); } catch (NamingException e) { 
    throw new ConnectorException(e); } } // 重命名/修改路径 LdapContext ctx = conn.getInitialContext().newInstance(null); ctx.addToEnvironment("java.naming.ldap.deleteRDN", deleteOldRdn); ctx.rename(oldName, newName); // 组操作 组操作通过 member / memberOf 属性进行操作 

五 . 特殊操作

5.1 User 权限操作

// 通过在 BasicAttributes 中添加 userAccountControl 属性实现设置用户的权限 public static final String UACCONTROL_ATTR = "userAccountControl"; 

权限的设置并不是任意的 , 部分权限只能由特定的子权限跳上去 ,AD 提供了如下权限 :

image.png

需要注意的是 , AD 权限的互转不是任意的 , 下图前四个权限只有指定的权限才能转过去 , 其他的权限可以互转!!

image.png

5.2 Group 权限操作

// 通过在 BasicAttributes 中添加 groupType 属性实现设置组的权限 public static final String LDAP_GROUP_TYPE = "groupType"; 

image.png

5.3 国籍操作

// AD 中有多个属性来控制国际 public static final String COUNTRY = "c"; public static final String COUNTRY_NAME = "co"; public static final String COUNTRY_CODE = "countryCode"; BasicAttribute c = new BasicAttribute(COUNTRY,countryCode.getCountrySign()); BasicAttribute co = new BasicAttribute(COUNTRY_NAME,countryCode.getCountryName()); BasicAttribute code = new BasicAttribute(COUNTRY_CODE,countryCode.getCode()); 

image.png

5.4 连接池问题

参考文档 @ https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html

该文档中对连接池做了很详细的描述

// 常规配置连接池的方式 : env.put("com.sun.jndi.ldap.connect.pool","true"); env.put("com.sun.jndi.ldap.connect.pool.authentication", "simple"); env.put("com.sun.jndi.ldap.connect.pool.maxsize", "3"); env.put("com.sun.jndi.ldap.connect.pool.prefsize", "1"); env.put("com.sun.jndi.ldap.connect.pool.timeout", ""); 

image.png

5.5 AD 特殊字符

AD 中可以通过ASCII 码传入特殊字符 ,例如可以将名称 , 但是要注意的是 = 或者类似的符号 ,在 AD 中本身就存在 , 他在生成的时候 , AD会自动添加 / 转义 , 所以对于我们使用的时候 , 就需要把自动转义的加上!!

image.png

总结

大概整理了一点皮毛 ,因为不想跑虚拟机 , 很多暂时没录上 , 后续如果有机会会考虑放上去 (太小众了 , 也不知道有没有人看)

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/126373.html

(0)
上一篇 2025-09-19 15:15
下一篇 2025-09-19 15:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信