java通过ldap同步ad域组织架构、人员信息

编程入门 行业动态 更新时间:2024-10-09 12:30:29

java通过ldap同步ad域<a href=https://www.elefans.com/category/jswz/34/1760321.html style=组织架构、人员信息"/>

java通过ldap同步ad域组织架构、人员信息

踩坑点:

  1. 普通操作389端口就可以,如果需要操作用户密码等,需要ssl链接,636端口,可以用证书方式,也可以用免证书方式,免证书也需要在ad域服务器生成对应的证书,参考:AD域证书申请,导入Java密钥库,实现ldap修改AD用户密码_寒沨的博客-CSDN博客_ad证书导出
  2. objectGUID是ad域里信息的唯一id,但是存储时16进制,需要进行转换,转换方式见下方代码
  3. 根据objectGUID进行查询的时候,也需要进行转换
  4. 部门查询时,如果查询的上级部门也不存在时,会报错
  5. 部门新增时,如果部门名称有'/',用public DirContext createSubcontext(String name, Attributes attrs);这个方法创建,会创建失败,可以用CompositeName自动转义,public DirContext createSubcontext(Name name, Attributes attrs);这个方法进行创建就可以
    1. //用string类型创建部门的时候,有特殊字符‘/’会创建失败,借用CompositeName可以自动转义‘/’
      Name composite = new CompositeName().add(distinguishedName);//添加
      ldapContext.createSubcontext(composite, attributes);
  6. 用ldapContext.search(searchBase, searchFilter,searchCtls)查询列表时,默认的分页列表最大1000条记录,如果要查询超过1000条数据,可以通过更改域的分页限制实现;通过env.put(Context.BATCHSIZE,"10000");也可以设置默认的查询条数,但是仅限于代码里设置的查询条数小于域里设置的条数
    1. 1.在“开始”——>“运行”——>输入“ ntdsutil”——>回车;2.输入:“ldap policies”,回车;3.输入:“connections”,回车;4.输入:“connect to domain yourDomainName”,例如(connect to domain baidu)5.连接提示出现后,输入:“quit”,回车;6.输入:“show values”,确认当前的最大返回数;(默认是1000)7.输入:“set MaxPageSize to 10000”,将最大返回数改为10000。(最大返回数可以根据实际情况自行定义)。8.再度输入:“show values”,确认当前的最大返回数(显示为:1000(10000))。9.输入“commit changes”以确认修改。10.
      再次输入:“show values”,确认当前的最大返回数为10000。11.
      输入“quit”,退出设置状态;12.
      输入“quit”,退出当前命令
  1. 用636端口ssl链接时很关键的一步,启动类上加,不然启动会报错javax.naming.CommunicationException: simple bind failed
    //ldap ssl链接很关键的
    System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification","true");
    1. 原因:为了提高LDAP(secureldap over TLS)连接的健壮性,默认情况下启用了端点识别算法。在某些情况下,以前能够成功连接到LDAPS服务器的某些应用程序可能不再能够这样做。如果这些应用程序认为合适,可以使用新的系统属性禁用端点标识:com.sun.jndi.ldap.object.disableEndpointIdentification。定义此系统属性(或将其设置为true)以禁用端点识别算法
    2. 本地idea启动的时候,idea vm options里设置”-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true“

代码:

  1. 登录
     /*** ssl方式免证书登录* System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification","true");这句很关键,将他放在启动类的main方法李* @return*/private LdapContext adLogin() {LdapContext ldapContext = null;Hashtable<String, Object> env = new Hashtable<String, Object>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");//验证类型env.put(Context.SECURITY_AUTHENTICATION, "simple");//用户名称,cn,ou,dc 分别:用户,组,域env.put(Context.SECURITY_PRINCIPAL, "username");//用户密码 cn 的密码env.put(Context.SECURITY_CREDENTIALS, "password");//url 格式:协议://ip:端口/组,域   ,直接连接到域或者组上面env.put(Context.PROVIDER_URL, "ldapurl");//协议env.put(Context.SECURITY_PROTOCOL, "ssl");env.put("java.naming.ldap.factory.socket", "DummySSLSocketFactory类全路径");//objectGUID 转换,很关键env.put("java.naming.ldap.attributes.binary","objectGUID");try {Control[] sortConnCtls = new SortControl[1];sortConnCtls[0] = new SortControl("sAMAccountName", Control.CRITICAL);ldapContext = new InitialLdapContext(env, sortConnCtls);} catch (IOException | NamingException e) {log.info("登录验证失败");e.printStackTrace();}return ldapContext;}
  2. 部门
        /*** 查询组织架构* @return* @throws Exception*/private List<JSONObject> getOU() throws Exception{LdapContext ldapContext = this.adLogin();//域部门节点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"};searchCtls.setReturningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部//  设置搜索范围 深度searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);//查询结果NamingEnumeration answer = ldapContext.search(searchBase, searchFilter,searchCtls);List<JSONObject> jsonObjectList = new ArrayList<>();while (answer.hasMoreElements()){SearchResult searchResult = (SearchResult) answer.next();Attributes attributes = searchResult.getAttributes();if(attributes != null){JSONObject jsonObject = new JSONObject();for(NamingEnumeration ne = attributes.getAll(); ne.hasMore();){Attribute attribute = (Attribute) ne.next();for (NamingEnumeration e = attribute.getAll(); e.hasMore();) {if("objectGUID".equals(attribute.getID())){String guid =this.getObjectGUID((byte[]) e.next());jsonObject.put(attribute.getID(), guid);}else {jsonObject.put(attribute.getID(), e.next().toString());}jsonObjectList.add(jsonObject);}}}}return jsonObjectList;}/*** guid转换(网上找了很多办法,这种最靠谱)* @param GUID* @return*/private String getObjectGUID(byte[] GUID){String strGUID = "";String byteGUID = "";for (int c=0;c<GUID.length;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 + "-";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" + Integer.toHexString(k) : Integer.toHexString(k);}/*** 组织架构新增* @throws Exception*/private void addOU() throws Exception{LdapContext ldapContext = this.adLogin();String distinguishedName = "ou=新增部门名称,OU=顶级跟目录,DC=test,DC=com";//新增部门Attributes attributes = new BasicAttributes();//类名attributes.put("objectClass","organizationalUnit");//显示名称attributes.put("distinguishedName", distinguishedName);attributes.put("ou", "新增部门名称");//添加ldapContext.createSubcontext(distinguishedName, attributes);}/*** 组织架构修改* @throws Exception*/private void updateOU() throws Exception{LdapContext ldapContext = this.adLogin();String oldName = "ou=旧组织架构名称,OU=顶级跟目录,DC=test,DC=com";String newName = "ou=新组织架构名称,OU=顶级跟目录,DC=test,DC=com";try {ldapContext.rename(oldName, newName);log.info("修改成功!");} catch (Exception e) {log.info("修改失败!");e.printStackTrace();} finally {try{ldapContext.close();}catch (Exception e){e.printStackTrace();}}}/*** 组织架构删除* @throws Exception*/private void deleteOU() throws Exception{LdapContext ldapContext = this.adLogin();String name = "ou=新组织架构名称,OU=顶级跟目录,DC=test,DC=com";try {ldapContext.destroySubcontext(name);log.info("删除成功!");} catch (Exception e) {log.info("删除失败!");e.printStackTrace();} finally {try{ldapContext.close();}catch (Exception e){e.printStackTrace();}}}
  3. 用户
        private List<JSONObject> getUser() throws Exception{LdapContext ldapContext = this.adLogin();List<UserAdDTO> adDTOList = new ArrayList<>();String searchFilter = "objectclass=User";String searchBase = "OU=顶级跟目录,DC=test,DC=com";// 创建搜索控制器SearchControls searchCtls = new SearchControls();String[]  returnedAttrs={"givenName","name","distinguishedName","objectGUID", "objectCategory", "logonCount","sAMAccountName","userPrincipalName"};searchCtls.setReturningAttributes(returnedAttrs); //设置指定返回的字段,不设置则返回全部//  设置搜索范围 深度searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);//查询结果NamingEnumeration answer = ldapContext.search(searchBase, searchFilter,searchCtls);List<JSONObject> jsonObjectList = new ArrayList<>();while (answer.hasMoreElements()){SearchResult searchResult = (SearchResult) answer.next();Attributes attributes = searchResult.getAttributes();if(attributes != null){JSONObject jsonObject = new JSONObject();for(NamingEnumeration ne = attributes.getAll(); ne.hasMore();){Attribute attribute = (Attribute) ne.next();for (NamingEnumeration e = attribute.getAll(); e.hasMore();) {if("objectGUID".equals(attribute.getID())){String guid =this.getObjectGUID((byte[]) e.next());jsonObject.put(attribute.getID(), guid);}else {jsonObject.put(attribute.getID(), e.next().toString());}}}jsonObjectList.add(jsonObject);}}return jsonObjectList;}public void addUser() {LdapContext ldapContext = this.login();Attributes attributes = new BasicAttributes();attributes.put("objectclass", "user");attributes.put("givenName", "新增用户名称");attributes.put("cn", "新增用户名称");attributes.put("name", "新增用户名称");//登录名, userPrincipalName和sAMAccountName区别,两者都是登录名,都可用,userPrincipalName有后缀域名attributes.put("userPrincipalName", "登录账号" + "@test");attributes.put("sAMAccountName", "登录账号");//账号状态,设置为十进制512,启用状态attributes.put("userAccountControl", "512");//设置为0.用户首次登录需要改密码attributes.put("pwdLastSet", "0");//设置默认密码attributes.put("unicodePwd", createUnicodePassword("密码"));try {ldapContext.createSubcontext("cn=" + name + ",OU=顶级目录,DC=test,DC=com", attributes);log.info("添加成功!");} catch (NamingException e) {e.printStackTrace();}finally {try{ldapContext.close();}catch (Exception e){e.printStackTrace();}}}/*** 密码加密* @return*/private byte[] createUnicodePassword(String password){String quotedPassword = "\"" + password + "\"";byte[] newUnicodePassword = quotedPassword.getBytes(StandardCharsets.UTF_16LE);return newUnicodePassword;}/*** 修改用户*/public void updateUser() {LdapContext ldapContext = this.adLogin();String oldName = "CN=旧姓名,OU=根目录,OU=顶级目录,DC=test,DC=com";String newName = "CN=新姓名,OU=根目录,OU=顶级目录,DC=test,DC=com";try {ldapContext.rename(oldName, newName);log.info("员工修改成功!");} catch (NamingException e) {e.printStackTrace();}finally {try{ldapContext.close();}catch (Exception e){e.printStackTrace();}}}/*** 删除用户* @return*/public void deleteUser() {LdapContext ldapContext = this.adLogin();String name = "CN=要删除用户姓名,OU=根目录,OU=顶级目录,DC=test,DC=com";try {ldapContext.destroySubcontext(name);log.info("员工删除改成功!");} catch (NamingException e) {e.printStackTrace();}finally {try{ldapContext.close();}catch (Exception e){e.printStackTrace();}}}/*** 根据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:* \FF\64\D7\F8\6A\9A\8E\41\A6\41\B6\F9\96\61\A8\D5* @return*/public String convertGUID(String guidString){String GUID = "";String[] guidArray = guidString.split("-");for(int i =0; i<3; i++){String[] newStrArray = getSub(guidArray[i], 2);ArrayUtils.reverse(newStrArray);guidArray[i] = StringUtils.join(newStrArray);}String[] guidArr = getSub(StringUtils.join(guidArray), 2);for(int i=0; i<guidArr.length; i++){String str = guidArr[i];guidArr[i] = "\\" + str;}GUID = StringUtils.join(guidArr);System.out.println(GUID);return GUID;}private static String[] getSub(String orgStr, int length){if(Objects.isNull(orgStr)){return null;}int n = (orgStr.length() + length - 1) / length;String[] newStr = new String[n];for(int i = 0; i < n; i++){if(i < (n-1)){newStr[i] = orgStr.substring(i * length, (i+1)*length);}else {newStr[i] = orgStr.substring(i * length);}}return newStr;}
  4. 跳过登录类(DummySSLSocketFactory和DummyTrustManager)
    1. DummySSLSocketFactory

      import javax.SocketFactory;
      import javax.ssl.SSLContext;
      import javax.ssl.SSLSocketFactory;
      import javax.ssl.TrustManager;
      import java.io.IOException;
      import java.InetAddress;
      import java.Socket;public class DummySSLSocketFactory extends SSLSocketFactory {private SSLSocketFactory factory;public DummySSLSocketFactory() {try {SSLContext sslcontext = SSLContext.getInstance("TLS");sslcontext.init( null, // No KeyManager requirednew TrustManager[] { new com.bananain.auth.config.DummyTrustManager()},new java.security.SecureRandom());factory = ( SSLSocketFactory) sslcontext.getSocketFactory();} catch( Exception ex) { ex.printStackTrace(); }}public static SocketFactory getDefault() {return new DummySSLSocketFactory();}public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException {return factory.createSocket( socket, s, i, flag);}public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {return factory.createSocket( inaddr, i, inaddr1, j);}public Socket createSocket( InetAddress inaddr, int i) throws IOException {return factory.createSocket( inaddr, i);}public Socket createSocket( String s, int i, InetAddress inaddr, int j) throws IOException {return factory.createSocket( s, i, inaddr, j);}public Socket createSocket( String s, int i) throws IOException {return factory.createSocket( s, i);}public String[] getDefaultCipherSuites() {return factory.getSupportedCipherSuites();}public String[] getSupportedCipherSuites() {return factory.getSupportedCipherSuites();}
      }

    2. DummyTrustManager

      import javax.ssl.X509TrustManager;
      import java.security.cert.X509Certificate;public class DummyTrustManager implements X509TrustManager {public void checkClientTrusted( X509Certificate[] cert, String authType) {return;}public void checkServerTrusted( X509Certificate[] cert, String authType) {return;}public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
      }

  5. 其它

更多推荐

java通过ldap同步ad域组织架构、人员信息

本文发布于:2024-02-13 13:34:51,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1758583.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:组织架构   人员   信息   java   ldap

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!