2023年6月21日发(作者:)
java通过ldap同步ad域组织架构、⼈员信息踩坑点:1. 普通操作389端⼝就可以,如果需要操作⽤户密码等,需要ssl链接,636端⼝,可以⽤证书⽅式,也可以⽤免证书⽅式,免证书也需要在ad域服务器⽣成对应的证书,参考:2. objectGUID是ad域⾥信息的唯⼀id,但是存储时16进制,需要进⾏转换,转换⽅式见下⽅代码3. 根据objectGUID进⾏查询的时候,也需要进⾏转换4. 部门查询时,如果查询的上级部门也不存在时,会报错5. ⽤636端⼝ssl链接时很关键的⼀步,启动类上加,不然启动会报错icationException: simple bind failed//ldap ssl链接很关键的 perty("eEndpointIdentification","true");代码:1. 登录 /** * ssl⽅式免证书登录 * perty("eEndpointIdentification","true");这句很关键,将他放在启动类的main⽅法李 * @return */ private LdapContext adLogin() { LdapContext ldapContext = null; Hashtable env = new Hashtable(); (L_CONTEXT_FACTORY, "xFactory"); //验证类型 (TY_AUTHENTICATION, "simple"); //⽤户名称,cn,ou,dc 分别:⽤户,组,域 (TY_PRINCIPAL, "username"); //⽤户密码 cn 的密码 (TY_CREDENTIALS, "password"); //url 格式:协议://ip:端⼝/组,域 ,直接连接到域或者组上⾯ (ER_URL, "ldapurl"); //协议 (TY_PROTOCOL, "ssl"); ("", "SLSocketFactory"); //objectGUID 转换,很关键 ("","objectGUID"); try { Control[] sortConnCtls = new SortControl[1]; sortConnCtls[0] = new SortControl("sAMAccountName", AL); ldapContext = new InitialLdapContext(env, sortConnCtls); } catch (IOException | NamingException e) { ("登录验证失败"); tackTrace(); } return ldapContext; }2. 部门 /** * 查询组织架构 * @return * @throws Exception */ private List getOU() throws Exception{ LdapContext ldapContext = n(); LdapContext ldapContext = n(); //域部门节点 String searchBase = "OU=顶级跟⽬录,DC=test,DC=com"; //搜索条件 String searchFilter = "objectclass=organizationalUnit";// String searchFilter = "(&(objectclass=organizationalUnit)(|(name=名称1)(name=名称2)))"; //查询部门,并且部门名称等于名称1或者名称2 // 创建搜索控制器 SearchControls searchCtls = new SearchControls(); String[] returnedAttrs={"ou", "name","canonicalName","distinguishedName","objectGUID", "objectCategory"}; urningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部 // 设置搜索范围 深度 rchScope(E_SCOPE); //查询结果 NamingEnumeration answer = (searchBase, searchFilter,searchCtls); List jsonObjectList = new ArrayList<>(); while (eElements()){ SearchResult searchResult = (SearchResult) (); Attributes attributes = ributes(); if(attributes != null){ JSONObject jsonObject = new JSONObject(); for(NamingEnumeration ne = (); e();){ Attribute attribute = (Attribute) (); for (NamingEnumeration e = (); e();) { if("objectGUID".equals(())){ String guid =ectGUID((byte[]) ()); ((), guid); }else { ((), ().toString()); } (jsonObject); } } } } return jsonObjectList; } /** * guid转换(⽹上找了很多办法,这种最靠谱) * @param GUID * @return */ private String getObjectGUID(byte[] GUID){ String strGUID = ""; String byteGUID = ""; for (int c=0;c<;c++) { byteGUID = byteGUID + "" + AddLeadingZero((int)GUID[c] & 0xFF); } strGUID = strGUID + AddLeadingZero((int)GUID[3] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[2] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[1] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[0] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[5] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[4] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[7] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[6] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[8] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[9] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[9] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[10] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[11] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[12] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[13] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[14] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[15] & 0xFF); return strGUID; } private static String AddLeadingZero(int k) { return (k <= 0xF) ? "0" + tring(k) : Integer .toHexString(k); } /** * 组织架构新增 * @throws Exception */ private void addOU() throws Exception{ LdapContext ldapContext = n(); String distinguishedName = "ou=新增部门名称,OU=顶级跟⽬录,DC=test,DC=com"; //新增部门 Attributes attributes = new BasicAttributes(); //类名 ("objectClass","organizationalUnit"); //显⽰名称 ("distinguishedName", distinguishedName); ("ou", "新增部门名称"); //添加 Subcontext(distinguishedName, attributes); } /** * 组织架构修改 * @throws Exception */ private void updateOU() throws Exception{ LdapContext ldapContext = n(); String oldName = "ou=旧组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; String newName = "ou=新组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; try { (oldName, newName); ("修改成功!"); } catch (Exception e) { ("修改失败!"); tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 组织架构删除 * @throws Exception * @throws Exception */ private void deleteOU() throws Exception{ LdapContext ldapContext = n(); String name = "ou=新组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; try { ySubcontext(name); ("删除成功!"); } catch (Exception e) { ("删除失败!"); tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } }3. ⽤户 private List getUser() throws Exception{ LdapContext ldapContext = n(); List adDTOList = new ArrayList<>(); String searchFilter = "objectclass=User"; String searchBase = "OU=顶级跟⽬录,DC=test,DC=com"; // 创建搜索控制器 SearchControls searchCtls = new SearchControls(); urningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部 // 设置搜索范围 深度 rchScope(E_SCOPE); //查询结果 NamingEnumeration answer = (searchBase, searchFilter,searchCtls); List jsonObjectList = new ArrayList<>(); while (eElements()){ SearchResult searchResult = (SearchResult) (); Attributes attributes = ributes(); if(attributes != null){ JSONObject jsonObject = new JSONObject(); for(NamingEnumeration ne = (); e();){ Attribute attribute = (Attribute) (); for (NamingEnumeration e = (); e();) { if("objectGUID".equals(())){ String guid =ectGUID((byte[]) ()); ((), guid); }else { ((), ().toString()); } } } (jsonObject); } } return jsonObjectList; } String[] returnedAttrs={"givenName","name","distinguishedName","objectGUID", "objectCategory", "logonCount","sAMAccountName","userPrincipalNa } public void addUser() { LdapContext ldapContext = (); Attributes attributes = new BasicAttributes(); ("objectclass", "user"); ("givenName", "新增⽤户名称"); ("cn", "新增⽤户名称"); ("name", "新增⽤户名称"); //登录名, userPrincipalName和sAMAccountName区别,两者都是登录名,都可⽤,userPrincipalName有后缀域名 ("userPrincipalName", "登录账号" + "@"); ("sAMAccountName", "登录账号"); //账号状态,设置为⼗进制512,启⽤状态 ("userAccountControl", "512"); //设置为0.⽤户⾸次登录需要改密码 ("pwdLastSet", "0"); //设置默认密码 ("unicodePwd", createUnicodePassword("密码")); try { Subcontext("cn=" + name + ",OU=顶级⽬录,DC=test,DC=com", attributes); ("添加成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 密码加密 * @return */ private byte[] createUnicodePassword(String password){ String quotedPassword = """ + password + """; byte[] newUnicodePassword = es(_16LE); return newUnicodePassword; } /** * 修改⽤户 */ public void updateUser() { LdapContext ldapContext = n(); String oldName = "CN=旧姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; String newName = "CN=新姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; try { (oldName, newName); ("员⼯修改成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } } /** * 删除⽤户 * @return */ public void deleteUser() { LdapContext ldapContext = n(); String name = "CN=要删除⽤户姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; try { ySubcontext(name); ("员⼯删除改成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 根据guid查询⽤户时,需要对guid进⾏转换 * 转换步骤 * first uppercase the GUID: * F8D764FF-9A6A-418E-A641-B6F99661A8D5 * * split it on each dash into five parts: * F8D764FF, 9A6A, 418E, A641, B6F99661A8D5 * * split each part into bytes (two hex digits each): * {F8, D7, 64, FF}, {9A, 6A}, {41, 8E}, {A6, 41}, {B6, F9, 96, 61, A8, D5} * * reverse the bytes of the first three parts: * {FF, 64, D7, F8}, {6A, 9A}, {8E, 41}, {A6, 41}, {B6, F9, 96, 61, A8, D5} * * disregard the division into parts: * FF, 64, D7, F8, 6A, 9A, 8E, 41, A6, 41, B6, F9, 96, 61, A8, D5 * * prepend a backslash to every byte: * FF, 64, D7, F8, 6A, 9A, 8E, 41, A6, 41, B6, F9, 96, 61, A8, D5 * * concatenate the bytes: * FF64D7F86A9A8E41A641B6F99661A8D5 * @return */ public String convertGUID(String guidString){ String GUID = ""; String[] guidArray = ("-"); for(int i =0; i<3; i++){ String[] newStrArray = getSub(guidArray[i], 2); e(newStrArray); guidArray[i] = (newStrArray); } String[] guidArr = getSub((guidArray), 2); for(int i=0; i<; i++){ for(int i=0; i<; i++){ String str = guidArr[i]; guidArr[i] = "" + str; } GUID = (guidArr); n(GUID); return GUID; } private static String[] getSub(String orgStr, int length){ if((orgStr)){ return null; } int n = (() + length - 1) / length; String[] newStr = new String[n]; for(int i = 0; i < n; i++){ if(i < (n-1)){ newStr[i] = ing(i * length, (i+1)*length); }else { newStr[i] = ing(i * length); } } return newStr; }
2023年6月21日发(作者:)
java通过ldap同步ad域组织架构、⼈员信息踩坑点:1. 普通操作389端⼝就可以,如果需要操作⽤户密码等,需要ssl链接,636端⼝,可以⽤证书⽅式,也可以⽤免证书⽅式,免证书也需要在ad域服务器⽣成对应的证书,参考:2. objectGUID是ad域⾥信息的唯⼀id,但是存储时16进制,需要进⾏转换,转换⽅式见下⽅代码3. 根据objectGUID进⾏查询的时候,也需要进⾏转换4. 部门查询时,如果查询的上级部门也不存在时,会报错5. ⽤636端⼝ssl链接时很关键的⼀步,启动类上加,不然启动会报错icationException: simple bind failed//ldap ssl链接很关键的 perty("eEndpointIdentification","true");代码:1. 登录 /** * ssl⽅式免证书登录 * perty("eEndpointIdentification","true");这句很关键,将他放在启动类的main⽅法李 * @return */ private LdapContext adLogin() { LdapContext ldapContext = null; Hashtable env = new Hashtable(); (L_CONTEXT_FACTORY, "xFactory"); //验证类型 (TY_AUTHENTICATION, "simple"); //⽤户名称,cn,ou,dc 分别:⽤户,组,域 (TY_PRINCIPAL, "username"); //⽤户密码 cn 的密码 (TY_CREDENTIALS, "password"); //url 格式:协议://ip:端⼝/组,域 ,直接连接到域或者组上⾯ (ER_URL, "ldapurl"); //协议 (TY_PROTOCOL, "ssl"); ("", "SLSocketFactory"); //objectGUID 转换,很关键 ("","objectGUID"); try { Control[] sortConnCtls = new SortControl[1]; sortConnCtls[0] = new SortControl("sAMAccountName", AL); ldapContext = new InitialLdapContext(env, sortConnCtls); } catch (IOException | NamingException e) { ("登录验证失败"); tackTrace(); } return ldapContext; }2. 部门 /** * 查询组织架构 * @return * @throws Exception */ private List getOU() throws Exception{ LdapContext ldapContext = n(); LdapContext ldapContext = n(); //域部门节点 String searchBase = "OU=顶级跟⽬录,DC=test,DC=com"; //搜索条件 String searchFilter = "objectclass=organizationalUnit";// String searchFilter = "(&(objectclass=organizationalUnit)(|(name=名称1)(name=名称2)))"; //查询部门,并且部门名称等于名称1或者名称2 // 创建搜索控制器 SearchControls searchCtls = new SearchControls(); String[] returnedAttrs={"ou", "name","canonicalName","distinguishedName","objectGUID", "objectCategory"}; urningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部 // 设置搜索范围 深度 rchScope(E_SCOPE); //查询结果 NamingEnumeration answer = (searchBase, searchFilter,searchCtls); List jsonObjectList = new ArrayList<>(); while (eElements()){ SearchResult searchResult = (SearchResult) (); Attributes attributes = ributes(); if(attributes != null){ JSONObject jsonObject = new JSONObject(); for(NamingEnumeration ne = (); e();){ Attribute attribute = (Attribute) (); for (NamingEnumeration e = (); e();) { if("objectGUID".equals(())){ String guid =ectGUID((byte[]) ()); ((), guid); }else { ((), ().toString()); } (jsonObject); } } } } return jsonObjectList; } /** * guid转换(⽹上找了很多办法,这种最靠谱) * @param GUID * @return */ private String getObjectGUID(byte[] GUID){ String strGUID = ""; String byteGUID = ""; for (int c=0;c<;c++) { byteGUID = byteGUID + "" + AddLeadingZero((int)GUID[c] & 0xFF); } strGUID = strGUID + AddLeadingZero((int)GUID[3] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[2] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[1] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[0] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[5] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[4] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[7] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[6] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[8] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[9] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[9] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int)GUID[10] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[11] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[12] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[13] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[14] & 0xFF); strGUID = strGUID + AddLeadingZero((int)GUID[15] & 0xFF); return strGUID; } private static String AddLeadingZero(int k) { return (k <= 0xF) ? "0" + tring(k) : Integer .toHexString(k); } /** * 组织架构新增 * @throws Exception */ private void addOU() throws Exception{ LdapContext ldapContext = n(); String distinguishedName = "ou=新增部门名称,OU=顶级跟⽬录,DC=test,DC=com"; //新增部门 Attributes attributes = new BasicAttributes(); //类名 ("objectClass","organizationalUnit"); //显⽰名称 ("distinguishedName", distinguishedName); ("ou", "新增部门名称"); //添加 Subcontext(distinguishedName, attributes); } /** * 组织架构修改 * @throws Exception */ private void updateOU() throws Exception{ LdapContext ldapContext = n(); String oldName = "ou=旧组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; String newName = "ou=新组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; try { (oldName, newName); ("修改成功!"); } catch (Exception e) { ("修改失败!"); tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 组织架构删除 * @throws Exception * @throws Exception */ private void deleteOU() throws Exception{ LdapContext ldapContext = n(); String name = "ou=新组织架构名称,OU=顶级跟⽬录,DC=test,DC=com"; try { ySubcontext(name); ("删除成功!"); } catch (Exception e) { ("删除失败!"); tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } }3. ⽤户 private List getUser() throws Exception{ LdapContext ldapContext = n(); List adDTOList = new ArrayList<>(); String searchFilter = "objectclass=User"; String searchBase = "OU=顶级跟⽬录,DC=test,DC=com"; // 创建搜索控制器 SearchControls searchCtls = new SearchControls(); urningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部 // 设置搜索范围 深度 rchScope(E_SCOPE); //查询结果 NamingEnumeration answer = (searchBase, searchFilter,searchCtls); List jsonObjectList = new ArrayList<>(); while (eElements()){ SearchResult searchResult = (SearchResult) (); Attributes attributes = ributes(); if(attributes != null){ JSONObject jsonObject = new JSONObject(); for(NamingEnumeration ne = (); e();){ Attribute attribute = (Attribute) (); for (NamingEnumeration e = (); e();) { if("objectGUID".equals(())){ String guid =ectGUID((byte[]) ()); ((), guid); }else { ((), ().toString()); } } } (jsonObject); } } return jsonObjectList; } String[] returnedAttrs={"givenName","name","distinguishedName","objectGUID", "objectCategory", "logonCount","sAMAccountName","userPrincipalNa } public void addUser() { LdapContext ldapContext = (); Attributes attributes = new BasicAttributes(); ("objectclass", "user"); ("givenName", "新增⽤户名称"); ("cn", "新增⽤户名称"); ("name", "新增⽤户名称"); //登录名, userPrincipalName和sAMAccountName区别,两者都是登录名,都可⽤,userPrincipalName有后缀域名 ("userPrincipalName", "登录账号" + "@"); ("sAMAccountName", "登录账号"); //账号状态,设置为⼗进制512,启⽤状态 ("userAccountControl", "512"); //设置为0.⽤户⾸次登录需要改密码 ("pwdLastSet", "0"); //设置默认密码 ("unicodePwd", createUnicodePassword("密码")); try { Subcontext("cn=" + name + ",OU=顶级⽬录,DC=test,DC=com", attributes); ("添加成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 密码加密 * @return */ private byte[] createUnicodePassword(String password){ String quotedPassword = """ + password + """; byte[] newUnicodePassword = es(_16LE); return newUnicodePassword; } /** * 修改⽤户 */ public void updateUser() { LdapContext ldapContext = n(); String oldName = "CN=旧姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; String newName = "CN=新姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; try { (oldName, newName); ("员⼯修改成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } } /** * 删除⽤户 * @return */ public void deleteUser() { LdapContext ldapContext = n(); String name = "CN=要删除⽤户姓名,OU=根⽬录,OU=顶级⽬录,DC=test,DC=com"; try { ySubcontext(name); ("员⼯删除改成功!"); } catch (NamingException e) { tackTrace(); } finally { try{ (); }catch (Exception e){ tackTrace(); } } } /** * 根据guid查询⽤户时,需要对guid进⾏转换 * 转换步骤 * first uppercase the GUID: * F8D764FF-9A6A-418E-A641-B6F99661A8D5 * * split it on each dash into five parts: * F8D764FF, 9A6A, 418E, A641, B6F99661A8D5 * * split each part into bytes (two hex digits each): * {F8, D7, 64, FF}, {9A, 6A}, {41, 8E}, {A6, 41}, {B6, F9, 96, 61, A8, D5} * * reverse the bytes of the first three parts: * {FF, 64, D7, F8}, {6A, 9A}, {8E, 41}, {A6, 41}, {B6, F9, 96, 61, A8, D5} * * disregard the division into parts: * FF, 64, D7, F8, 6A, 9A, 8E, 41, A6, 41, B6, F9, 96, 61, A8, D5 * * prepend a backslash to every byte: * FF, 64, D7, F8, 6A, 9A, 8E, 41, A6, 41, B6, F9, 96, 61, A8, D5 * * concatenate the bytes: * FF64D7F86A9A8E41A641B6F99661A8D5 * @return */ public String convertGUID(String guidString){ String GUID = ""; String[] guidArray = ("-"); for(int i =0; i<3; i++){ String[] newStrArray = getSub(guidArray[i], 2); e(newStrArray); guidArray[i] = (newStrArray); } String[] guidArr = getSub((guidArray), 2); for(int i=0; i<; i++){ for(int i=0; i<; i++){ String str = guidArr[i]; guidArr[i] = "" + str; } GUID = (guidArr); n(GUID); return GUID; } private static String[] getSub(String orgStr, int length){ if((orgStr)){ return null; } int n = (() + length - 1) / length; String[] newStr = new String[n]; for(int i = 0; i < n; i++){ if(i < (n-1)){ newStr[i] = ing(i * length, (i+1)*length); }else { newStr[i] = ing(i * length); } } return newStr; }
发布评论