最近因工作需要自己花时间学习了ldap的API并在项目中使用了,其中ldap开发有自己的API,现在的java自带的API也包含了相关的API
ldap自身的API:https://www.novell.com/documentation/developer/jldap/jldapenu/api/
jdk自带的api在
javax.naming.*包下面
其中openldap有些比较坑的地方,页面上创建Entry和修改Entry的时候的字段名不一致,然后使用java开发的时候又不一致
其中添加用户的时候需要添加属性(objectClass=posixAccount)
添加组的时候需要添加属性(objectClass=posixGroup)
其中在使用java添加的时候javaAPI中需要添加的属性与openldap页面添加的属性名对应关系有
User Name 对应属性 uid
Password 对应属性 userPassword
如果不一致会报如下错误error code 17 - User Name :AttributeDescription contains in appropriate characters
添加组有gid,添加用户有uid,通过页面添加的时候可以发现他们的id应该是自增的,但是你查出来之后,设置属性必须使用字符串设置进去,否则会报Malformed gidNumber 错误,当然这个属性名也是页面上的
下面是通过javaAPI写的示例
package com.java.ldap;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class LdapTest {
private static final String BASE_DN = "dc=csair,dc=com";
private static final String USER_DN_BASE = "ou=users,dc=csair,dc=com";
private static final String GROUP_DN_BASE = "ou=group,dc=csair,dc=com";
public static void main(String[] args) throws Exception {
LdapContext connectLdap = connectLdap();
getMaxId(connectLdap);
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
NamingEnumeration<SearchResult> search = connectLdap.search("cn=athenatest111,ou=group,dc=abc,dc=com", "(objectClass=posixGroup)", null, searchCtls);
// NamingEnumeration<SearchResult> search = connectLdap.search("cn=caifan,ou=users,dc=abc,dc=com", "(objectClass=posixAccount)", null,searchCtls);
while (search.hasMore()) {
SearchResult result = search.next();
//System.out.println(result.getAttributes().get("memberUid").get(2));//.get("description")
System.out.println(result.getAttributes().get("gidnumber").get());//.get("description")
System.out.println(result.getAttributes().get("objectClass"));
System.out.println(result.getName());
}
getAllGroups(connectLdap);
}
public static void getAllGroups(LdapContext context) throws Exception {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//SHIRO-115 - prevent potential code injection:
String searchFilter = "(&(objectClass=posixGroup))";
NamingEnumeration answer = context.search("ou=group,dc=abc,dc=com", searchFilter, null, searchCtls);
while (answer.hasMoreElements()) {
SearchResult searchResult = (SearchResult) answer.next();
String group = searchResult.getName().substring(3);
//组名
String groupName = "";
Attribute groupNameAttr = searchResult.getAttributes().get("description");
System.out.println(group + "::" + groupNameAttr + "::" + searchResult.getAttributes().get("memberUid"));
//组成员
List<String> userIds = new ArrayList<>();
Attribute memberUidAttr = searchResult.getAttributes().get("memberUid");
if (memberUidAttr != null) {
NamingEnumeration memberUid = memberUidAttr.getAll();
while (memberUid.hasMore()) {
String userId = (String) memberUid.next();
userIds.add(userId);
}
}
}
}
public static Integer getMaxId(LdapContext context) throws Exception {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
List<Integer> idList = new ArrayList<>();
//NamingEnumeration<SearchResult> search = context.search("ou=group,dc=abc,dc=com", "(objectClass=posixGroup)", null,searchCtls);
NamingEnumeration<SearchResult> search = context.search("ou=users,dc=abc,dc=com", "(objectClass=posixAccount)", null,searchCtls);
while (search.hasMore()) {
SearchResult result = search.next();
//System.out.println(result.getAttributes().get("memberUid").get(2));//.get("description")
//System.out.println(result.getAttributes().get("gidnumber").get());//.get("description")
idList.add(Integer.parseInt(result.getAttributes().get("uidnumber").get().toString()));
//System.out.println(result.getAttributes().get("uidnumber").get());
}
Collections.sort(idList);
for(Integer id : idList) {
System.out.println(id);
}
System.out.println("maxID:" + idList.get(idList.size() -1));
return idList.get(0);
}
public static LdapContext connectLdap() throws Exception {
// 连接Ldap需要的信息
String ldapFactory = "com.sun.jndi.ldap.LdapCtxFactory";
String ldapUrl = "ldap://ip:389";// url
String ldapAccount = "cn=admin,dc=abc,dc=com"; // 用户名
String ldapPwd = "password";//密码
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, ldapFactory);
// LDAP server
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, ldapAccount);
env.put(Context.SECURITY_CREDENTIALS, ldapPwd);
env.put("java.naming.referral", "follow");
LdapContext ctxTDS = new InitialLdapContext(env, null);
return ctxTDS;
}
public void addUser(String cn, String givenName, String password, List<String> group) throws NamingException {
LdapContext ldapContext = null;
try {
ldapContext = connectLdap();
Integer uidNumber = getMaxId(ldapContext, USER_DN_BASE) + 1;
String md5 = generateMD5(password);
Attributes attributes = new BasicAttributes();
Attribute passwordAttribute = new BasicAttribute("userPassword", md5);
Attribute objectClass = new BasicAttribute("objectClass", true);
objectClass.add("inetOrgPerson");
objectClass.add("posixAccount");
objectClass.add("top");
Attribute cnAttr = new BasicAttribute("cn",cn);
Attribute givenNameAttr = new BasicAttribute("givenName",givenName);
Attribute homeDirectoryAttr = new BasicAttribute("homeDirectory", "/home/users/" + cn);
Attribute loginShellAttr = new BasicAttribute("loginShell", "/bin/sh");
Attribute snAttr = new BasicAttribute("sn", cn);
Attribute uidAttr = new BasicAttribute("uid", cn);
Attribute uidNumberAttr = new BasicAttribute("uidNumber", uidNumber + "");
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
if(group.size() > 0) {
NamingEnumeration<SearchResult> searchGroup = ldapContext.search("cn=" + group.get(0) + "," + GROUP_DN_BASE, "(objectClass=posixGroup)", null, searchCtls);
while (searchGroup.hasMore()) {
String gid = searchGroup.next().getAttributes().get("gidnumber").get().toString();
Attribute gidAttr = new BasicAttribute("gidNumber", gid);
attributes.put(gidAttr);
}
//添加用户到组
group.forEach(groupName -> {
try {
moveUser2Group(cn, groupName, 0);
} catch (Exception e) {
}
});
}
attributes.put(cnAttr);
attributes.put(givenNameAttr);
attributes.put(passwordAttribute);
attributes.put(objectClass);
attributes.put(loginShellAttr);
attributes.put(snAttr);
attributes.put(homeDirectoryAttr);
attributes.put(uidAttr);
attributes.put(uidNumberAttr);
ldapContext.createSubcontext("cn=" + cn + "," + USER_DN_BASE, attributes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(ldapContext != null) {
ldapContext.close();
}
}
}
/**
* * 其中givenName sn UserName在修改的时候可以添加成多个值,添加的时候是通过其他值进行转换的,其中User Name sn在修改的时候最后至少保留一个,否则报错
* @param cn 用户唯一标识
* @param givenName 添加的givenName属性
* @param addGroup 添加的组
* @param subGroup 减少的组
*/
public void modifyUser(String cn, String givenName, List<String> addGroup, List<String> subGroup) {
LdapContext ldapContext = null;
try {
ldapContext = connectLdap();
Attributes attributes = new BasicAttributes();
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
NamingEnumeration<SearchResult> search = ldapContext.search("cn=" + cn + "," + USER_DN_BASE, "(objectClass=posixAccount)", null, searchControls);
while (search.hasMore()) {
SearchResult searchResult = search.next();
Attributes beforeAttrs = searchResult.getAttributes();
Attribute givenNameAttrs = beforeAttrs.get("givenName");
if(givenNameAttrs != null && givenNameAttrs.get() != null && !givenNameAttrs.get().toString().trim().equals(givenName.trim())) {
givenNameAttrs.clear();
givenNameAttrs.add(givenName);
attributes.put(givenNameAttrs);
} else {
givenNameAttrs = new BasicAttribute("givenName", givenName);
attributes.put(givenNameAttrs);
}
Attribute uidAttrs = beforeAttrs.get("uid");
if(addGroup != null && !addGroup.isEmpty()) {
if(addGroup.get(0).trim().length() > 0) {
NamingEnumeration<SearchResult> searchResults = ldapContext.search("cn=" + addGroup.get(0) + "," + GROUP_DN_BASE, "(objectClass=posixGroup)", null, searchControls);
Attribute gidAttribute = null;
while (searchResults.hasMore()) {
SearchResult sr = searchResults.next();
gidAttribute = sr.getAttributes().get("gidNumber");
attributes.put(new BasicAttribute("gidNumber", gidAttribute.get()));
}
addGroup.forEach(add -> {
try {
if (add.trim().length() > 0) {
moveUser2Group(cn, add, 0);
}
} catch (Exception e) {
}
});
}
}
if (subGroup != null && subGroup.size() > 0) {
subGroup.forEach(sub -> {
try {
if(sub.trim().length() > 0) {
moveUser2Group(cn, sub, 1);
}
} catch (Exception e) {
}
});
}
ldapContext.modifyAttributes("cn=" + cn + "," + USER_DN_BASE, DirContext.REPLACE_ATTRIBUTE, attributes);
}
} catch (Exception e) {
} finally {
if(ldapContext != null) {
try {
ldapContext.close();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private void modifyList(Attribute attrs, List<String> addValue, List<String> subValue) {
if(attrs != null ) {
if(addValue != null && addValue.size() > 0) {
for (String add : addValue) {
if (!attrs.contains(add)) {
attrs.add(add);
}
}
}
if(subValue != null && subValue.size() > 0) {
for (String sub : subValue) {
if (attrs.contains(sub)) {
attrs.remove(sub);
}
}
}
}
}
public void deleteUser(String username) throws Exception {
LdapContext context = null;
try {
context = connectLdap();
context.destroySubcontext("cn=" + username + "," + USER_DN_BASE);
} catch (NamingException e) {
} finally {
if(context != null) {
context.close();;
}
}
}
/**
* 添加用户到指定的组
* @param userId
* @param groupName
* @param type 0:添加 1:移除
* @throws Exception
*/
private void moveUser2Group (String userId, String groupName, Integer type) throws Exception {
LdapContext context = connectLdap();
String searchFilter = "(&(objectClass=posixGroup))";
SearchControls searchCtrl = new SearchControls();
searchCtrl.setSearchScope(SearchControls.OBJECT_SCOPE);
NamingEnumeration answer = context.search("cn=" + groupName + "," + GROUP_DN_BASE, searchFilter, null, searchCtrl);
SearchResult searchResult;
while (answer.hasMore()) {
searchResult = (SearchResult) answer.next();
Attribute attribute = searchResult.getAttributes().get("memberUid");
if(attribute == null) {
attribute = new BasicAttribute("memberUid");
}
if (type == 0 && !attribute.contains(userId)) {
attribute.add(attribute.size(), userId);
} else if(type == 1 && attribute.contains(userId)) {
attribute.remove(userId);
}
ModificationItem[] item = new ModificationItem[1];
item[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attribute);
context.modifyAttributes("cn=" + groupName + "," + GROUP_DN_BASE, item);
}
}
private Integer getMaxId(LdapContext context, String dn) throws Exception {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
List<Integer> idList = new ArrayList<>();
NamingEnumeration<SearchResult> search = null;
if(GROUP_DN_BASE.equals(dn)) {
search = context.search(GROUP_DN_BASE, "(objectClass=posixGroup)", null,searchCtls);
while (search.hasMore()) {
SearchResult result = search.next();
idList.add(Integer.parseInt(result.getAttributes().get("gidnumber").get().toString()));
}
} else if(USER_DN_BASE.equals(dn)) {
search = context.search(USER_DN_BASE, "(objectClass=posixAccount)", null,searchCtls);
while (search.hasMore()) {
SearchResult result = search.next();
idList.add(Integer.parseInt(result.getAttributes().get("uidnumber").get().toString()));
}
}
Collections.sort(idList);
return idList.get(idList.size() -1);
}
public static String generateMD5(String password) {
//声明StringBuffer对象来存放最后的值
StringBuffer sb=new StringBuffer();
try {
//1.初始化MessageDigest信息摘要对象,并指定为MD5不分大小写都可以
MessageDigest md=MessageDigest.getInstance("md5");
//2.传入需要计算的字符串更新摘要信息,传入的为字节数组byte[],
//将字符串转换为字节数组使用getBytes()方法完成
//指定时其字符编码 为utf-8
md.update(password.getBytes("utf-8"));
//3.计算信息摘要digest()方法
//返回值为字节数组
byte [] hashCode=md.digest();
//4.将byte[] 转换为找度为32位的16进制字符串
//遍历字节数组
for(byte b:hashCode){
//对数组内容转化为16进制,
sb.append(Character.forDigit(b>>4&0xf, 16));
//换2次为32位的16进制
sb.append(Character.forDigit(b&0xf, 16));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return sb.toString();
}
}
后面通过ldapAPI写示例
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.novell.ldap.util.DN;
/**
* 通过LDAPConnection获取词条
* @author admin
*
*/
public class LdapUtils {
private static String ldapHost = "IP";
private static int ldapPort = 389;
private static String ldapBindDN = "cn=admin,dc=abc,dc=com";
private static String ldapPassword = "password";
private static LDAPConnection connection = null;
public static void main(String[] args) throws Exception {
DN dn = new DN(ldapBindDN);
int countResults = 0;
openConnection();
//LDAPSearchResults results = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_BASE, "objectClass=*", null, false);//获取当前DN级别LDAPEntry
//LDAPSearchResults results1 = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_ONE, "objectClass=*", null, false);//获取当前DN级别的子集LDAPEntry
LDAPSearchResults results2 = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_SUB, "objectClass=*", null, false);//获取所有DN级别的子集
System.out.println("===results1======");
while(results2.hasMore()) {
System.out.println(results2.next());
countResults++;
}
System.out.println(countResults);
connection.disconnect();
}
/**
* 添加LDAPEntry
* @param connection
* @param dn
* @param attrList
* @throws LDAPException
*/
public void addLDAPEntry(LDAPConnection connection, String dn, List<LDAPAttribute> attrList) throws LDAPException {
LDAPAttributeSet ldapAttributeSet = new LDAPAttributeSet();
ldapAttributeSet.addAll(attrList);
LDAPEntry ldapEntry = new LDAPEntry(dn, ldapAttributeSet);
connection.add(ldapEntry);
connection.disconnect();
}
/**
* 修改LDAP属性
* @param connection
* @param dn
* @param attrList
* @param type LDAPModification。REPLACE DELETE
* @throws LDAPException
*/
public static void modifyLDAPEntryAttr(LDAPConnection connection, String dn, List<LDAPAttribute> attrList, int type) throws LDAPException {
LDAPModification ldapModification;
if(attrList != null && attrList.size() > 0) {
for(LDAPAttribute attr : attrList) {
ldapModification = new LDAPModification(type, attr);
connection.modify(dn, ldapModification);
}
}
}
/**
* 连接LDAP
*/
public static void openConnection() {
if (connection == null) {
try {
connection = new LDAPConnection();
connection.connect(ldapHost, ldapPort);
connection.bind(LDAPConnection.LDAP_V3, ldapBindDN, ldapPassword.getBytes("UTF8"));
} catch (Exception e) {
System.out.println("连接LDAP出现错误:\n" + e.getMessage());
System.exit(1);
}
}
}
/**
* 根据查询scope 获取所有的子LDAPEntry scope:LDAPConnection.SCOPE_BASE(查询当前级LdapEnttry) LDAPConnection.SCOPE_ONE(查询当前子集LDAPEntry) LDAPConnection.SCOPE_SUB(查询所有的子集LDAPEntry,如有子目录则递归)
* @param dn
* @param connection
* @param searchFilter 搜索条件 通常为"objectClass=*"
* @param attrs 属性
* @return
* @throws LDAPException
*/
public static Map<String, Object> getLDAPEntry(String dn, LDAPConnection connection, int scope, String searchFilter, String[] attrs) throws LDAPException {
Map<String, Object> resultsMap = new HashMap<>();
List<LDAPEntry> entryList = new ArrayList<>();
int resultsCounts = 0;
LDAPSearchResults ldapSearchResults = connection.search(dn, scope, searchFilter, attrs, false);
LDAPEntry ldapEntry;
while(ldapSearchResults.hasMore()) {
ldapEntry = ldapSearchResults.next();
entryList.add(ldapEntry);
resultsCounts++;
}
resultsMap.put("ldapEntryCounts", resultsCounts);
resultsMap.put("ldapEntryList", entryList);
return resultsMap;
}
/**
* 根据连接和DN获取LDAPEntry
* @param connection
* @param ldapBindDN
* @return
* @throws Exception
*/
public static LDAPEntry getLDAPEntry(LDAPConnection connection, String ldapBindDN) throws Exception {
return connection.read(ldapBindDN);
}
/**
* @param connection
* @param userDN
* @param groupDN
* @return
* 添加用户到组
* @throws LDAPException
*/
public static boolean addUser2Group(LDAPConnection connection, String userDN, String groupDN, List<LDAPAttribute> userAttributes, List<LDAPAttribute> groupAtrributes) {
try {
modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.ADD);
} catch (LDAPException e) {
try {
modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.DELETE);
} catch (LDAPException e1) {
e1.printStackTrace();
}
System.out.println(e.getMessage());
return false;
}
try {
modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.ADD);
} catch (LDAPException e) {
try {
modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.DELETE);
} catch (LDAPException e1) {
e1.printStackTrace();
}
System.out.println(e.getMessage());
return false;
}
return true;
}
}
有疑问欢迎加入:513650703群聊,有更好的意欢迎提出分享。