最近因工作需要自己花时间学习了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群聊,有更好的意欢迎提出分享。
相关推荐
Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...
Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...
java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....