2023年6月21日发(作者:)

使⽤JAVA控制AD域进⾏基本的操作详解前段时间,我接到了⼀个新任务:使⽤JAVA控制AD域进⾏基本的操作。经⼏天摸索也看来多位博友的代码,现在摸索出了⼀套较为详细的Java操控AD域的⽅法。废话不多说,现进⼊正题》》》⼀、安装域控服务器⼆、安装证书控制服务 1. AD域控安装证书服务 2. 从ad域上拿ca证书⽂件 1)点链接:下载 CA证书、证书链或CRL 2)选择编码base64 3)点链接:下载 CA证书 ps:这⾥采⽤的是结合ad服务器上的证书颁发机构,如果要采⽤第三⽅机构发布的证书需要另⾏配置,详情看我的另⼀篇⽂章。 3. 根据CA证书⽣成keystore⽂件三、ping通虚拟机我相信⼤部分⼈做这个开发应该是在虚拟机上做开发测试的,ping通虚拟机是必不可少的。四、编写Java代码如果你完成了上述步骤,那么恭喜你,基本的环境你已经搭建完成了,现在可以开始我们的开发。⾸先介绍⼀下AD域的⽤户存储,实际上就是⼀个ldap的⽬录存储过程,所以我们可以根据对ldap的访问模式访问到ad域上的对象,例如我想访问AD与上名为上海的OU下的⼀个⽤户test1,那么我可以访问“CN=test1,OU=上海,DC=adserv,DC=com”(后⾯的DC是我的域控服务器的名字如:)。知道了如何访问指定对象,那么接下来的事情不是变得简单了?下⾯是我借鉴了⽹上的⼀些⼤⽜的代码,⾃⼰编写的操作类如有什么异常,⾮常欢迎您的指点哦1.第⼀步 创建Ldap连接,因为AD域的连接控制本质上就是对Ldap的控制,所以获取了ldap的连接就是获取了AD域的操作权。2.第⼆步 完成上述代码之后下⾯的增删查改,只要通过getConnect()⽅法获取链接就可以进⾏操作。

a.添加⽤户操作新建⽤户的话⼀般新建在⼀个OU(组织结构)中,⽽我的需求中是要对在添加⽤户的时候输⼊⽤户名和所属OU,⽤户不存在则创建,若该⽤户存在就移⼊新的OU,如果OU不存就逐层创建OU再保存这个⽤户。⽤户信息的话是使⽤⼀个Attributes对象进⾏包装由前台传进来的(这⾥需要说明的是,每⼀个对象都有⾃⼰特定的类型,所以我们在创建对象时必须要确定创建的类型,就是下⽂的type,我将他提取出来),具体的对象属性可以参考这篇⽂章《》还有对新的⽤户账号的权限设置《》,下⾯是实现代码: public static boolean addUser(String userOU, String userName,String passWord, Attributes attrs, String type)throws NamingException { final int UF_PASSWD_NOTREQD = 0x0020; final int UF_NORMAL_ACCOUNT = 0x0200; final int UF_PASSWORD_EXPIRED = 0x800000; LdapContext ctx = null; String userDN = ""; try {

if (!"organizationalUnit".equals(type)) { userDN = "CN=" + userName + ","; } else { userDN = "OU=" + userName + ","; } ctx = getConn(); // create the organizational structure that does not exist String[] OU = (","); String ou = ""; for (int i = - 1; i >= 0; i--) { ou = "OU=" + OU[i] + "," + ou; ou = "OU=" + OU[i] + "," + ou; if (i == 0) userDN = userDN + ou + "DC=adserv,DC=com"; String OUDN = ou + "DC=adserv,DC=com"; try { ributes(OUDN); } catch (NameNotFoundException e) { addOU(OUDN); } } try { if ("organizationalUnit".equals(type)) { ("objectClass", "organizationalUnit"); Subcontext(userDN, attrs); return true; } // required attributes ("objectClass", type); ("sAMAccountName", userName); Subcontext(userDN, attrs); } catch (NameAlreadyBoundException e) { if("organizationalUnit".equals(type)){ n("The organizational unit already exists"); return false; }else{ String oldDN = searchEntity(userName, type); changeOU(oldDN, ou); modifyUser(attrs, userName, type); n("User already exists, has been moved into the organization unit"); } } if(passWord!=null){ ModificationItem[] mods = new ModificationItem[2]; // format the password String newQuotedPassword = """ + passWord + """; //format password byte[] newUnicodePassword = es("UTF-16LE"); mods[0] = new ModificationItem(E_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword)); mods[1] = new ModificationItem(E_ATTRIBUTE,

new BasicAttribute("userAccountControl", ng(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED + UF_PASSWD_NOTREQD))); // save the password Attributes(userDN, mods); mods = null;} return true; } catch (NamingException e) { tackTrace(); } catch (IOException e) { tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return false; }ps:如果使⽤的389接⼝的话使⽤上述代码还是能实现创建对象,但是创建时会报错(据我估计就是安全错误,要求你使⽤ssl),并且创建的对象时⽆法设置密码的,但是如果你们可以不使⽤ssl设置密码的话请教教我,谢谢。、b.⽤户删除删除操作较为简单,确定删的对象类型(对象类型会在下⾯内容中介绍)就可以了。代码如下: public static boolean delEntity(String entityName,String entityType) throws NamingException { LdapContext ctx = null; try{ String DN=searchEntity(entityName,entityType); ctx=getConn(); if(DN!=null){ ySubcontext(DN); }else{ n("delete Entity not exist"); return false; } return true; } catch (Exception e) { n("Problem: " + e); } finally { if (ctx != null) { (); ctx = null; } } return false; }

c.查找对象 查找兑现,与上次对象⼀样,要确定对象的类型,并且要确定对象名称。 我收集了⼏个对象的类型表达: ⽤户:(&(objectCategory=person)(objectClass=user); 策划机:computer;组:group;

接洽⼈:contact ;共享⽂件夹:volume; 打印机:printQueue 下⾯是查找的实现代码: public static String searchEntity(String entityName, String entityType) throws NamingException { LdapContext ctx = null; String OUName = ""; try { ctx = getConn(); SearchControls searchCtls = new SearchControls(); rchScope(E_SCOPE); String searchFilter = null; if ("user".equals(entityType)) { searchFilter = "(&(objectCategory=person)(objectClass=user)(cn=" + entityName + "))"; } else { String[] OUList = (","); entityName = ""; for (int i = - 1; i >= 0; i--) { entityName = "OU=" + OUList[i] + "," + entityName; OUName = OUList[0]; } searchFilter = "(&(objectCategory=organizationalUnit)(objectclass=organizationalUnit)(OU=" + OUName + "))"; } String searchBase = "DC=adserv,DC=com"; String returnedAtts[] = { "memberOf" }; urningAttributes(returnedAtts); NamingEnumeration answer = (searchBase, searchFilter, searchCtls); SearchResult sr = null; while (eElements()) { sr = (SearchResult) (); if (!"".equals(OUName)) return entityName + "DC=adserv,DC=com"; return e() + ",DC=adserv,DC=com"; } } catch (NullPointerException e) { n("Entity not exist"); } catch (Exception e) { tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return null; }c.修改对象

修改对象的属性可以同个对对象的数据进⾏修改,封装传⼊,程序会⽐较两个对象⽐较修改过得地⽅修改保存,为修改的就不修改代码如下: public static boolean modifyUser(Attributes attrs, String entityName,String entityType) throws NamingException { LdapContext ctx = null; try { String userDN=searchEntity(entityName,entityType); ctx=getConn(); Attributes(userDN, E_ATTRIBUTE, attrs); return true; } catch (Exception e) { n("Problem: " + e); } finally { if (ctx != null) { try { (); } catch (NamingException e) { tackTrace(); } ctx = null; } } return false; }修改对象的组织结构则是对对象名字进⾏修改: public static boolean changeOU(String olduserDN, String newOU) throws NamingException { LdapContext ctx =null; try{ ctx=getConn(); String[] user=(","); String newDN=user[0]+","+newOU+user[-2]+","+user[-1]; (olduserDN,newDN); return true; }catch(Exception e){ tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return false; }好了,今天暂时给⼤家介绍到这⾥,之后我会简单的介绍⼀下组等其他对象的操作。⽬前,我能做已经能做到对AD域上的⼤部分对象的处理,如组,打印机,联系⼈,上⾯这⼀段代码,复制进⼊就能⽤了,有什么问题需要讨论可以随时发私信给我。-----------------------------2017/12/5----------------------------------------有读者说有⼀部分代码没有发出来,拉下了不好意思,现在补上 addOU:添加组织机构⽅法public static boolean addOU(String OUDN) throws NamingException {LdapContext ctx = null;try {ctx = getConn();// create a new OUAttributes attrs = new BasicAttributes(true);("objectClass", "organizationalUnit");Subcontext(OUDN, attrs);return true;} catch (Exception e) {tackTrace();} finally {if (ctx != null) {();ctx = null;}}return false;}getConn:获取ad域连接;getConnectionFromFool:获取ad域连接池

/** * 获取连接通道,如果是636接⼝就返回加密通道,否则返回普通通道 * @param keyStore keyStore的⽂件⽬录 * @param sslPassWord keyStore的密码 * @param adPassWord ad域密码 * @param ldapUrl ad域地址 * @param port 使⽤的接⼝类型636/389 * @return * @throws NamingException */public static LdapContext getConnectionFromFool(String keyStore, String sslPassWord, String adPassWord,String ldapUrl, String port) throws NamingException {Properties env = new Properties();if ("636".equals(port)) {perty("tore", keyStore);perty("torePassword", sslPassWord);(TY_PROTOCOL, "ssl");}(L_CONTEXT_FACTORY, "xFactory");(ER_URL, "ldap://" + ldapUrl);(TY_AUTHENTICATION, "simple");(TY_PRINCIPAL, "cn=Administrator,cn=Users,dc=adserv,dc=com");//第⼀个cn为你的ad域管理员账户名(TY_CREDENTIALS, adPassWord);("", "true");("al", "follow");return new InitialLdapContext(env, null);}public static LdapContext getConn() throws NamingException {//这边就是读取配置⽂件中的接⼝// String port = null;// if (tance().useSsl) {// port = SSL_LDAP_URL_Port;// } else {// port = WITHOUT_SSL_LDAP_URL_Port;// } 下⾯为389连接 return getConnectionFromFool(KEYSTORE, SSL_PASSWORD, AD_PASSWORD, LDAP_URL, 389);}构造⽤户对象⽤于测试Attributes attrs = new BasicAttributes(true);("Title", "⼯程师");("Department", "部门");("Company", "机构");("objectClass","user");("userPrincipalName",userName + "@");

2023年6月21日发(作者:)

使⽤JAVA控制AD域进⾏基本的操作详解前段时间,我接到了⼀个新任务:使⽤JAVA控制AD域进⾏基本的操作。经⼏天摸索也看来多位博友的代码,现在摸索出了⼀套较为详细的Java操控AD域的⽅法。废话不多说,现进⼊正题》》》⼀、安装域控服务器⼆、安装证书控制服务 1. AD域控安装证书服务 2. 从ad域上拿ca证书⽂件 1)点链接:下载 CA证书、证书链或CRL 2)选择编码base64 3)点链接:下载 CA证书 ps:这⾥采⽤的是结合ad服务器上的证书颁发机构,如果要采⽤第三⽅机构发布的证书需要另⾏配置,详情看我的另⼀篇⽂章。 3. 根据CA证书⽣成keystore⽂件三、ping通虚拟机我相信⼤部分⼈做这个开发应该是在虚拟机上做开发测试的,ping通虚拟机是必不可少的。四、编写Java代码如果你完成了上述步骤,那么恭喜你,基本的环境你已经搭建完成了,现在可以开始我们的开发。⾸先介绍⼀下AD域的⽤户存储,实际上就是⼀个ldap的⽬录存储过程,所以我们可以根据对ldap的访问模式访问到ad域上的对象,例如我想访问AD与上名为上海的OU下的⼀个⽤户test1,那么我可以访问“CN=test1,OU=上海,DC=adserv,DC=com”(后⾯的DC是我的域控服务器的名字如:)。知道了如何访问指定对象,那么接下来的事情不是变得简单了?下⾯是我借鉴了⽹上的⼀些⼤⽜的代码,⾃⼰编写的操作类如有什么异常,⾮常欢迎您的指点哦1.第⼀步 创建Ldap连接,因为AD域的连接控制本质上就是对Ldap的控制,所以获取了ldap的连接就是获取了AD域的操作权。2.第⼆步 完成上述代码之后下⾯的增删查改,只要通过getConnect()⽅法获取链接就可以进⾏操作。

a.添加⽤户操作新建⽤户的话⼀般新建在⼀个OU(组织结构)中,⽽我的需求中是要对在添加⽤户的时候输⼊⽤户名和所属OU,⽤户不存在则创建,若该⽤户存在就移⼊新的OU,如果OU不存就逐层创建OU再保存这个⽤户。⽤户信息的话是使⽤⼀个Attributes对象进⾏包装由前台传进来的(这⾥需要说明的是,每⼀个对象都有⾃⼰特定的类型,所以我们在创建对象时必须要确定创建的类型,就是下⽂的type,我将他提取出来),具体的对象属性可以参考这篇⽂章《》还有对新的⽤户账号的权限设置《》,下⾯是实现代码: public static boolean addUser(String userOU, String userName,String passWord, Attributes attrs, String type)throws NamingException { final int UF_PASSWD_NOTREQD = 0x0020; final int UF_NORMAL_ACCOUNT = 0x0200; final int UF_PASSWORD_EXPIRED = 0x800000; LdapContext ctx = null; String userDN = ""; try {

if (!"organizationalUnit".equals(type)) { userDN = "CN=" + userName + ","; } else { userDN = "OU=" + userName + ","; } ctx = getConn(); // create the organizational structure that does not exist String[] OU = (","); String ou = ""; for (int i = - 1; i >= 0; i--) { ou = "OU=" + OU[i] + "," + ou; ou = "OU=" + OU[i] + "," + ou; if (i == 0) userDN = userDN + ou + "DC=adserv,DC=com"; String OUDN = ou + "DC=adserv,DC=com"; try { ributes(OUDN); } catch (NameNotFoundException e) { addOU(OUDN); } } try { if ("organizationalUnit".equals(type)) { ("objectClass", "organizationalUnit"); Subcontext(userDN, attrs); return true; } // required attributes ("objectClass", type); ("sAMAccountName", userName); Subcontext(userDN, attrs); } catch (NameAlreadyBoundException e) { if("organizationalUnit".equals(type)){ n("The organizational unit already exists"); return false; }else{ String oldDN = searchEntity(userName, type); changeOU(oldDN, ou); modifyUser(attrs, userName, type); n("User already exists, has been moved into the organization unit"); } } if(passWord!=null){ ModificationItem[] mods = new ModificationItem[2]; // format the password String newQuotedPassword = """ + passWord + """; //format password byte[] newUnicodePassword = es("UTF-16LE"); mods[0] = new ModificationItem(E_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword)); mods[1] = new ModificationItem(E_ATTRIBUTE,

new BasicAttribute("userAccountControl", ng(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED + UF_PASSWD_NOTREQD))); // save the password Attributes(userDN, mods); mods = null;} return true; } catch (NamingException e) { tackTrace(); } catch (IOException e) { tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return false; }ps:如果使⽤的389接⼝的话使⽤上述代码还是能实现创建对象,但是创建时会报错(据我估计就是安全错误,要求你使⽤ssl),并且创建的对象时⽆法设置密码的,但是如果你们可以不使⽤ssl设置密码的话请教教我,谢谢。、b.⽤户删除删除操作较为简单,确定删的对象类型(对象类型会在下⾯内容中介绍)就可以了。代码如下: public static boolean delEntity(String entityName,String entityType) throws NamingException { LdapContext ctx = null; try{ String DN=searchEntity(entityName,entityType); ctx=getConn(); if(DN!=null){ ySubcontext(DN); }else{ n("delete Entity not exist"); return false; } return true; } catch (Exception e) { n("Problem: " + e); } finally { if (ctx != null) { (); ctx = null; } } return false; }

c.查找对象 查找兑现,与上次对象⼀样,要确定对象的类型,并且要确定对象名称。 我收集了⼏个对象的类型表达: ⽤户:(&(objectCategory=person)(objectClass=user); 策划机:computer;组:group;

接洽⼈:contact ;共享⽂件夹:volume; 打印机:printQueue 下⾯是查找的实现代码: public static String searchEntity(String entityName, String entityType) throws NamingException { LdapContext ctx = null; String OUName = ""; try { ctx = getConn(); SearchControls searchCtls = new SearchControls(); rchScope(E_SCOPE); String searchFilter = null; if ("user".equals(entityType)) { searchFilter = "(&(objectCategory=person)(objectClass=user)(cn=" + entityName + "))"; } else { String[] OUList = (","); entityName = ""; for (int i = - 1; i >= 0; i--) { entityName = "OU=" + OUList[i] + "," + entityName; OUName = OUList[0]; } searchFilter = "(&(objectCategory=organizationalUnit)(objectclass=organizationalUnit)(OU=" + OUName + "))"; } String searchBase = "DC=adserv,DC=com"; String returnedAtts[] = { "memberOf" }; urningAttributes(returnedAtts); NamingEnumeration answer = (searchBase, searchFilter, searchCtls); SearchResult sr = null; while (eElements()) { sr = (SearchResult) (); if (!"".equals(OUName)) return entityName + "DC=adserv,DC=com"; return e() + ",DC=adserv,DC=com"; } } catch (NullPointerException e) { n("Entity not exist"); } catch (Exception e) { tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return null; }c.修改对象

修改对象的属性可以同个对对象的数据进⾏修改,封装传⼊,程序会⽐较两个对象⽐较修改过得地⽅修改保存,为修改的就不修改代码如下: public static boolean modifyUser(Attributes attrs, String entityName,String entityType) throws NamingException { LdapContext ctx = null; try { String userDN=searchEntity(entityName,entityType); ctx=getConn(); Attributes(userDN, E_ATTRIBUTE, attrs); return true; } catch (Exception e) { n("Problem: " + e); } finally { if (ctx != null) { try { (); } catch (NamingException e) { tackTrace(); } ctx = null; } } return false; }修改对象的组织结构则是对对象名字进⾏修改: public static boolean changeOU(String olduserDN, String newOU) throws NamingException { LdapContext ctx =null; try{ ctx=getConn(); String[] user=(","); String newDN=user[0]+","+newOU+user[-2]+","+user[-1]; (olduserDN,newDN); return true; }catch(Exception e){ tackTrace(); } finally { if (ctx != null) { (); ctx = null; } } return false; }好了,今天暂时给⼤家介绍到这⾥,之后我会简单的介绍⼀下组等其他对象的操作。⽬前,我能做已经能做到对AD域上的⼤部分对象的处理,如组,打印机,联系⼈,上⾯这⼀段代码,复制进⼊就能⽤了,有什么问题需要讨论可以随时发私信给我。-----------------------------2017/12/5----------------------------------------有读者说有⼀部分代码没有发出来,拉下了不好意思,现在补上 addOU:添加组织机构⽅法public static boolean addOU(String OUDN) throws NamingException {LdapContext ctx = null;try {ctx = getConn();// create a new OUAttributes attrs = new BasicAttributes(true);("objectClass", "organizationalUnit");Subcontext(OUDN, attrs);return true;} catch (Exception e) {tackTrace();} finally {if (ctx != null) {();ctx = null;}}return false;}getConn:获取ad域连接;getConnectionFromFool:获取ad域连接池

/** * 获取连接通道,如果是636接⼝就返回加密通道,否则返回普通通道 * @param keyStore keyStore的⽂件⽬录 * @param sslPassWord keyStore的密码 * @param adPassWord ad域密码 * @param ldapUrl ad域地址 * @param port 使⽤的接⼝类型636/389 * @return * @throws NamingException */public static LdapContext getConnectionFromFool(String keyStore, String sslPassWord, String adPassWord,String ldapUrl, String port) throws NamingException {Properties env = new Properties();if ("636".equals(port)) {perty("tore", keyStore);perty("torePassword", sslPassWord);(TY_PROTOCOL, "ssl");}(L_CONTEXT_FACTORY, "xFactory");(ER_URL, "ldap://" + ldapUrl);(TY_AUTHENTICATION, "simple");(TY_PRINCIPAL, "cn=Administrator,cn=Users,dc=adserv,dc=com");//第⼀个cn为你的ad域管理员账户名(TY_CREDENTIALS, adPassWord);("", "true");("al", "follow");return new InitialLdapContext(env, null);}public static LdapContext getConn() throws NamingException {//这边就是读取配置⽂件中的接⼝// String port = null;// if (tance().useSsl) {// port = SSL_LDAP_URL_Port;// } else {// port = WITHOUT_SSL_LDAP_URL_Port;// } 下⾯为389连接 return getConnectionFromFool(KEYSTORE, SSL_PASSWORD, AD_PASSWORD, LDAP_URL, 389);}构造⽤户对象⽤于测试Attributes attrs = new BasicAttributes(true);("Title", "⼯程师");("Department", "部门");("Company", "机构");("objectClass","user");("userPrincipalName",userName + "@");