Java 中 LDAP 的使用与整理
2018-01-10 本文已影响2888人
58bc06151329
文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
LDAP 是轻量 目录访问协议,英文全称是 Lightweight Directory Access Protocol,一般都简称为 LDAP。LDAP 基于 X.500 标准的,但简单了许多并且可以根据需要进行定制。与 X.500 不同,LDAP 支持 TCP/IP。LDAP 的核心规范在 RFC 中都有定义,所有与 LDAP 相关的RFC 都可以在 LDAPman RFC 网页中找到。
目录结构
- LDAP 目录以树状的层次结构来存储数据。
- 象 DNS 的 主机名,LDAP 目录记录的标识名(Distinguished Name,简称 DN)用来读取单个记录,以及回溯到树的顶部。
- LDAP 目录树的最顶部就是根,也就是所谓的 " 基准DN "。
- 基准 DN 通常使用三种格式之一
- 以 X.500 格式表示的基准 DN,例如 o=xxxx, c=CN
- 用公司的 Internet 地址表示的基准 DN,例如 o=xxxx.com
- 用 DNS 域名的不同部分组成的基准 DN,例如 dc=xxxx, dc=com
- 如果使用活动目录(Active Directory),Microsoft 限制必须使用 DNS 域名格式。
组织数据
- 在根目录下,数据从逻辑上区分开。
- 大多数 LDAP 目录用 OU 从逻辑上把数据分开来。OU 表示
Organization Unit ,在 X.500 协议中用来表示公司内部机构:销售部、财务部等等。 - 现在 LDAP 还保留 ou= 这样的命名规则,但是扩展了分类的范围,可以分类为:ou=people, ou=groups, ou=devices 等等。
- 更低一级的 OU 有时用来做更细的归类。
单独记录
- DN 是 LDAP 记录项的名字。
- 在 LDAP 目录中的所有记录项都有一个唯一的 Distinguished Name,也就是 DN。
- 每一个 LDAP 记录项的 DN 是由两个部分组成的
- 相对DN(RDN)
- 记录在 LDAP 目录中的位置。
- RDN 是 DN 中与目录树的结构无关的部分。
- 在 LDAP 目录中存储的记录项都要有一个名字,通常存在 cn(Common Name)属性里。
- 在 LDAP 中存储的对象都用 cn 值作为 RDN 的基础。
- 为公司的员工设置一个 DN
基于登录名(uid (User ID)与 unix 的 uid 完全不同)
uid=abc123, ou=xxxx, dc=xxxx, dc=com
基于姓名(cn)
cn=abc, ou=xxxx, dc=xxxx, dc=com
- 这种格式有明显的缺点,名字发生改变,LDAP 的记录就要从一个 DN 转移到另一个 DN。
定制对象
- LDAP 目录可以定制成存储任何文本或二进制数据。
- LDAP 目录用对象类型(object classes)的概念来定义运行哪一类的对象使用什么属性。
- LDAP 目录以一系列 " 属性对 " 的形式来存储记录项,每一个记录项包括属性类型和属性值(这与关系型数据库用行和列来存取数据有根本的不同)。
- LDAP 目录被设计成为一个属性保存多个值的,而不是在每一个属性的后面用逗号把一系列值分开。这样的方式存储数据,数据库有很大的灵活性,不必为加入一些新的数据就重新创建表和索引。
- LDAP 目录不必花费内存或硬盘空间处理 " 空 " 域,也就是说,实际上不使用可选择的域也不会花费任何资源。
- 记录项的格式是 LDIF,用来导入和导出 LDAP 目录的记录项。
- 属性的值在保存的时候是保留大小写的,但是在默认情况下搜索的时候是不区分大小写的。
- 某些特殊的属性(例如,password)在搜索的时候需要区分大小写。
属性名称 | 含义 | 备注 |
---|---|---|
OU | Organizational Unit(组织单元) | 最多可以有四级,每级最长 32 个字符,可以为中文。 |
DC | Domain Component(域名) | dc=xxxx,dc=com |
CN | Common Name(用户名或服务器名) | 最长可以到 80 个字符,可以为中文。 |
O | Organization(组织名称) | 可以 3~64 个字符长度。 |
C | Country(国家名) | 可选,为 2 个字符长度。 |
SN | 姓 | |
UID | 用户ID | |
DN | 唯一标识 | 类似于 Linux 文件系统的绝对路径,每个对象都有一个唯一的名称。 |
Java 中 LDAP 的基本操作
LDAP 连接
Hashtable env = new Hashtable();
String LDAP_URL = "ldap://<IP>:389"; // LDAP 访问地址
String adminName = "example\\user"; // 注意用户名的写法:domain\User 或 cn=user
String adminPassword = "userpassword"; // 密码
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, LDAP_URL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
try {
dc = new InitialDirContext(env);// 初始化上下文
System.out.println("认证成功");
} catch (javax.naming.AuthenticationException e) {
System.out.println("认证失败");
} catch (Exception e) {
System.out.println("认证出错:" + e);
}
LDAP 增加记录
String root = "dc=example,dc=com"; // LDAP的根节点的DC
BasicAttributes attrs = new BasicAttributes();
BasicAttribute objclassSet = new BasicAttribute("objectClass");
objclassSet.add("sAMAccountName");
objclassSet.add("employeeID");
objclassSet.add("sAMAccountName");
objclassSet.add("employeeID");
attrs.put(objclassSet);
attrs.put("ou", newUserName);
dc.createSubcontext("ou=" + newUserName + "," + root, attrs);
LDAP 删除记录
try {
dc.destroySubcontext(dn);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in delete():" + e);
}
LDAP 重命名节点
try {
dc.rename(oldDN, newDN);
return true;
} catch (NamingException ne) {
System.err.println("Error: " + ne.getMessage());
return false;
}
LDAP 修改
try {
ModificationItem[] mods = new ModificationItem[1];
/* 修改属性 */
// Attribute attr0 = new BasicAttribute("employeeID", "test");
// mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr0);
/* 删除属性 */
// Attribute attr0 = new BasicAttribute("description", "test");
// mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr0);
/* 添加属性 */
Attribute attr0 = new BasicAttribute("employeeID", employeeID);
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0);
/* 修改 */
dc.modifyAttributes(dn + ",dc=example,dc=com", mods);
return true;
} catch (NamingException e) {
e.printStackTrace();
System.err.println("Error: " + e.getMessage());
return false;
}
LDAP 查询
// 创建搜索控件
SearchControls searchCtls = new SearchControls();
// 设置搜索范围
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 设置搜索过滤条件
String searchFilter = "sAMAccountName=" + userName;
// 设置搜索域节点
String searchBase = "DC=example,DC=COM";
// 定制返回属性
String returnedAtts[] = { "url", "whenChanged", "employeeID", "name", "userPrincipalName", "physicalDeliveryOfficeName", "departmentNumber", "telephoneNumber", "homePhone", "mobile", "department", "sAMAccountName", "whenChanged", "mail" };
// 不定制属性,返回所有的属性集
// searchCtls.setReturningAttributes(null);
int totalResults = 0;
try {
NamingEnumeration answer = dc.search(searchBase, searchFilter, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes Attrs = sr.getAttributes();
if (Attrs != null) {
try {
for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {
Attribute Attr = (Attribute) ne.next();
// 读取属性值
for (NamingEnumeration e = Attr.getAll(); e.hasMore(); totalResults++) {
// 接受循环遍历读取的userPrincipalName用户属性
String user = e.next().toString();
}
// 读取属性值
// Enumeration values = Attr.getAll();
// if (values != null) {
// while (values.hasMoreElements()) {
// System.out.println(" 2AttributeValues=" + values.nextElement());
// }
// }
}
} catch (NamingException e) {
System.err.println("Throw Exception : " + e);
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Throw Exception : " + e);
}
- 搜索范围包含
- 本节点:SearchControls.OBJECT_SCOPE
- 单层:SearchControls.ONELEVEL_SCOPE
- 遍历:SearchControls.SUBTREE_SCOPE
LDAP 关闭
if (dc != null) {
try {
dc.close();
} catch (NamingException e) {
System.out.println("NamingException in close():" + e);
}
}