总文档 :文章目录
Github : github.com/black-ant
操作手册系列主要记录平时积累的无体系的操作笔记 , 这一篇主要是 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 : zhangsan@moonxy.com
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 ,我们可以通过对象的名称来找到和这个对象有关的所有信息
AD 主要成员 : AD 域对象和属性 : AD 域内的资源以对象(Object )的形式存在 ,通过Attriburte 来描述其特征,可以说对象本身就是属性的集合 DC :域控制器( Domain Controller ) : 域内可以有多台域控制器,每台域控制器地位平等 ,各自存储着一份相同的Active Directory 管理工具 : ctive Directory 用户和计算机 + Active Directory 管理中心
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, "123456");
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 有一套完整的查询语句 , 以下举例 @ www.ietf.org/rfc/rfc2254…
// 查询所有
(objectClass=*)
// objectGUID 查询
(&(objectClass=*)(objectGUID=c92131cee-dd23-445-9967-c462123455667))
(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 提供了如下权限 :
需要注意的是 , AD 权限的互转不是任意的 , 下图前四个权限只有指定的权限才能转过去 , 其他的权限可以互转!!
5.2 Group 权限操作
// 通过在 BasicAttributes 中添加 groupType 属性实现设置组的权限
public static final String LDAP_GROUP_TYPE = "groupType";
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());
5.4 连接池问题
参考文档 @ docs.oracle.com/javase/jndi…
该文档中对连接池做了很详细的描述
// 常规配置连接池的方式 :
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", "300000");
The default rule is that plain (non-SSL) connections that use simple or no authentication are allowed to be pooled 即连接池可能存在不会生效的可能 , 这个时候需要考虑修改配置
com.sun.jndi.ldap.connect.pool.protocol: 同时允许使用普通连接和SSL连接 com.sun.jndi.ldap.connect.pool.authentication : (none/simple/DIGEST-MD5)允许使用匿名(none)、简单和摘要- md5认证类型的连接池 , 需要配置
5.5 AD 特殊字符
AD 中可以通过ASCII 码传入特殊字符 ,例如可以将名称 , 但是要注意的是 = 或者类似的符号 ,在 AD 中本身就存在 , 他在生成的时候 , AD会自动添加 / 转义 , 所以对于我们使用的时候 , 就需要把自动转义的加上!!
总结
大概整理了一点皮毛 ,因为不想跑虚拟机 , 很多暂时没录上 , 后续如果有机会会考虑放上去 (太小众了 , 也不知道有没有人看)
今天的文章操作手册 : AD 及 LDAP 操作分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14121.html