package org.jd.test;import java.io.UnsupportedEncodingException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.util.Hashtable;import java.util.Vector;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.SearchControls;import javax.naming.directory.SearchResult;import javax.naming.ldap.InitialLdapContext;import javax.naming.ldap.LdapContext;/** * 非ssl方式擷取域的組織成員資訊 * @author ganjh * */public class AdTest {/** * 域:test.jd.com * user:hhx * password:12345678 * 域控伺服器ip:10.0.6.151 * @param name * @return * @throws UnsupportedEncodingException */public String GetADInfo(String name) throws UnsupportedEncodingException {String userName = name; // 使用者名稱稱if (userName == null) {userName = "";}Object company = "";String host = "10.0.6.151"; // AD伺服器String port = "389"; // 連接埠String url = new String("ldap://" + host + ":" + port);Hashtable HashEnv = new Hashtable();// String adminName ="CN=oyxiaoyuanxy,CN=Users,DC=Hebmc,DC=com";//AD的使用者名稱String adminName = "test\\hhx"; // 注意使用者名稱的寫法:domain\UserString adminPassword = "12345678"; // 密碼HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); // LDAP訪問安全層級HashEnv.put(Context.SECURITY_PRINCIPAL, adminName); // AD UserHashEnv.put(Context.SECURITY_CREDENTIALS, adminPassword); // AD PasswordHashEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); // LDAP工廠類HashEnv.put(Context.PROVIDER_URL, url);try {LdapContext ctx = new InitialLdapContext(HashEnv, null);// 域節點String searchBase = "OU=金地集團,DC=test,DC=jd,DC=com";// LDAP搜尋過濾器類String searchFilter = "objectClass=User";// 搜尋控制器SearchControls searchCtls = new SearchControls(); // Create the// search// controls// 建立搜尋控制器searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); // Specify// the// search// scope// 設定搜尋範圍// searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE); //// Specify the search scope 設定搜尋範圍// String returnedAtts[] = { "memberOf", "distinguishedName",// "Pwd-Last-Set", "User-Password", "cn" };// 定製返回屬性String returnedAtts[] = { "company","User-Password","cn","objectSID","objectGUID","description" };// 定製返回屬性// String returnedAtts[] = { "url", "whenChanged", "employeeID",// "name", "userPrincipalName", "physicalDeliveryOfficeName",// "departmentNumber", "telephoneNumber", "homePhone",// "mobile", "department", "sAMAccountName", "whenChanged",// "mail" }; // 定製返回屬性searchCtls.setReturningAttributes(returnedAtts); // 設定返回屬性集// 根據設定的域節點、過濾器類和搜尋控制器搜尋LDAP得到結果NamingEnumeration answer = ctx.search(searchBase, searchFilter,searchCtls);// Search for objects using the filter// 初始化搜尋結果數為0int totalResults = 0;// Specify the attributes to returnint rows = 0;while (answer.hasMoreElements()) {// 遍曆結果集SearchResult sr = (SearchResult) answer.next();// 得到符合搜尋條件的DNSystem.out.println(++rows+ "************************************************");String dn = sr.getName();System.out.println(dn);String match = dn.split("CN=")[1].split(",")[0];// 返回格式一般是CN=ptyh,OU=專賣System.out.println(match);//if (userName.equals(match)) {Attributes Attrs = sr.getAttributes();// 得到合格屬性集if (Attrs != null) {try {for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {Attribute Attr = (Attribute) ne.next();// 得到下一個屬性System.out.println(" AttributeID=屬性名稱:"+ Attr.getID().toString());if("objectGUID".equals(Attr.getID())){String st = getGUID(Attr.get().toString().getBytes());// 讀取屬性值for (NamingEnumeration e = Attr.getAll(); e.hasMore(); totalResults++) {//Vector c = (Vector)e.next();//System.out.println(c.get(0));company = new String(e.next().toString().getBytes("GB2312"),"utf-8");System.out.println(" AttributeValues=屬性值:"+ new String(company.toString().getBytes("GB2312"),"utf-8"));}}System.out.println(" ---------------");}} catch (NamingException e) {System.err.println("Throw Exception : " + e);}//}// if}}// whileSystem.out.println("************************************************");System.out.println("Number: " + totalResults);ctx.close();} catch (NamingException e) {e.printStackTrace();System.err.println("Throw Exception : " + e);}return company.toString();}private static String getGUID(byte[] inArr) {StringBuffer guid = new StringBuffer();for (int i = 0; i < inArr.length; i++) {StringBuffer dblByte = new StringBuffer(Integer.toHexString(inArr[i] & 0xff));if (dblByte.length() == 1) {guid.append("0");}guid.append(dblByte);}return guid.toString();}public static void main(String args[]) throws Exception {// 執行個體化AdTest ad = new AdTest();String company = ad.GetADInfo("huanghx");//Class.forName("oracle.jdbc.driver.OracleDriver"); //Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","eas","kingdee"); //String sql = "insert into ct_cos_test(fid,objectguid) values('11111','"+company+"')";//PreparedStatement ps = conn.prepareStatement(sql);////System.out.println(ps.executeUpdate());//////rs.close();//ps.close();//conn.close();//}}
補充:
網上通過ldap操作AD的例子很多,我也是通過網路搜尋然後成功的搜尋了公司未知結構的AD,中間經曆了一些波折,下面總結一下過程,我相信對需要操作AD的碼工碼農們多多少少是有些協助。
1 擷取DirContext要注意的地方。
以下是構造DirContext的基本代碼:
Java代碼
- DirContext ctx = null;
- String ldapURL = "ldap://10.0.15.1:389";
- String user = "test@xxx.com";
- String password = "restart#123";
- Hashtable env = new Hashtable();
- env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put(Context.PROVIDER_URL, ldapURL);
- env.put(Context.SECURITY_PRINCIPAL, user);
- env.put(Context.SECURITY_CREDENTIALS, password);
- ctx = new InitialDirContext(env);
DirContext ctx = null; String ldapURL = "ldap://10.0.15.1:389"; String user = "test@xxx.com"; String password = "restart#123"; Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.PROVIDER_URL, ldapURL); env.put(Context.SECURITY_PRINCIPAL, user); env.put(Context.SECURITY_CREDENTIALS, password); ctx = new InitialDirContext(env);
(1) 對AD結構是未知的情況下,ldapURL的寫法保守一點比較好,所以不在連接埠後加DN。
(2) 為什麼不用網域名稱,而用IP,因為有時候通過網域名稱訪問不到AD,存在不穩定情況,擷取域地址方法,通過cmd輸入ipconfig -all找到win server既是所需ip,可能會存在多個,一般大點的公司會有多個網域控制站。
(3)使用者名稱的寫法,不能直接用使用者名稱,而要加上域資訊,如userid@domain address或domain address\userid方式,如test@xxx.com,test是域帳號,xxx.com為AD網域名稱,否則會報異常。
2 查詢要注意的地方。
因為AD結構未知,所以查詢仍然要保守點。
Java代碼
- DirContext cnt = null;
- try
- {
- cnt = this.getContext();
- String base = "dc=xxx,dc=com";
- String filter = "(&(objectClass=user)(sAMAccountName=*test*))";
- int limitsize = 1;
- SearchControls searchCons = new SearchControls();
- NamingEnumeration namingEnum = null;
- searchCons.setSearchScope(2);
- searchCons.setCountLimit(limitsize);
- searchCons.setTimeLimit(0);
- namingEnum = cnt.search(base, filter, searchCons);
- print(namingEnum, base, limitsize);
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- finally
- {
- if (cnt != null)
- {
- cnt.close();
- }
- }
DirContext cnt = null; try { cnt = this.getContext(); String base = "dc=xxx,dc=com"; String filter = "(&(objectClass=user)(sAMAccountName=*test*))"; int limitsize = 1; SearchControls searchCons = new SearchControls(); NamingEnumeration namingEnum = null; searchCons.setSearchScope(2); searchCons.setCountLimit(limitsize); searchCons.setTimeLimit(0); namingEnum = cnt.search(base, filter, searchCons); print(namingEnum, base, limitsize); } catch (Exception e) { e.printStackTrace(); } finally { if (cnt != null) { cnt.close(); } }
(1) 未知AD情況下base開始唯寫出dc,因為域帳號通常不會直接建立在user節點下,一般會自己建立組織。
(2) 過濾器條件越少越好,而且最好用模糊比對,如String filter = sAMAccountName=*test*",其中test為登入帳號名。
(3)searchCons.setSearchScope(2),設為2會查詢子節點。
(4) searchCons.setTimeLimit(0),設逾時時間為0標識沒有逾時限制。
(5)因為過濾條件比較簡單而且是模糊條件,此時基本上能查出想要的資料,但節點數量很大的話查詢會比較慢,此時可以根據查詢的結果資訊來補充DN和filter,如在DN中加入OU根,在filter加上多個條件,filter加多個條件的方法(&(條件1)(條件2))。
(6)searchCons.setCountLimit(limitsize)問題,有時查詢時會報limitsize的異常,這是在遍曆查詢結果時出現的問題,下面是遍曆的部分代碼:
while (namingEnum != null && namingEnum.hasMore())
可以手工設定一個limitsize,當while迴圈次數到達limitsize時跳出while迴圈。
(7)注意關閉DirContext
3 遍曆結果要注意的問題。
(1) 時間的處理
Java代碼
- private String getConvertTime(Object time)
- {
- SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- if (time == null ||
"".equalsIgnoreCase(time.toString().trim()))
- {
- return "";
- }
- String strTime = time.toString().trim();
-
- if (strTime.indexOf(".") != -1)
- {
- strTime = strTime.substring(0, strTime.indexOf("."));
- }
- long longTime = Long.valueOf(strTime);
-
- GregorianCalendar Win32Epoch = new GregorianCalendar(1601, Calendar.JANUARY,
1);
- Win32Epoch.setTimeZone(TimeZone.getTimeZone("China"));
- Date Win32EpochDate = Win32Epoch.getTime();
- long TimeSinceWin32Epoch = longTime /
10000 + Win32EpochDate.getTime();
- Date lastLogon = new Date(TimeSinceWin32Epoch);
- return sf.format(lastLogon);
-
- }
private String getConvertTime(Object time) { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); if (time == null || "".equalsIgnoreCase(time.toString().trim())) { return ""; } String strTime = time.toString().trim(); if (strTime.indexOf(".") != -1) { strTime = strTime.substring(0, strTime.indexOf(".")); } long longTime = Long.valueOf(strTime); GregorianCalendar Win32Epoch = new GregorianCalendar(1601, Calendar.JANUARY, 1); Win32Epoch.setTimeZone(TimeZone.getTimeZone("China")); Date Win32EpochDate = Win32Epoch.getTime(); long TimeSinceWin32Epoch = longTime / 10000 + Win32EpochDate.getTime(); Date lastLogon = new Date(TimeSinceWin32Epoch); return sf.format(lastLogon); }
這個時間是基于格林威治1601年1月1日的,這要處理兩個問題,a:加上1601年1月1日這個基礎時間-Win32EpochDate.getTime(),b:格林威治時間與你所在時區有位移量(Win32Epoch.setTimeZone(TimeZone.getTimeZone("China"));
),所以要加減位移量才是真正的時間。
(2) lastLogon與lastLogonTimestamp,其中lastLogon至少在一台AD上是即時更新的,而lastLogonTimestamp則不是通常半個月才會更新,lastLogon因為存在在不同AD上的同步問題,所以需要在所有AD上都找到lastLogon,並找出最大值才是最後的登入時間。
(3)id類的處理,id屬性值是一串位元據,需要進行轉換字串。
- private static String getGUID(byte[] inArr)
- {
- StringBuffer guid = new StringBuffer();
- for (int i =
0; i < inArr.length; i++)
- {
- StringBuffer dblByte = new StringBuffer(Integer.toHexString(inArr[i] &
0xff));
- if (dblByte.length() == 1)
- {
- guid.append("0");
- }
- guid.append(dblByte);
- }
- return guid.toString();
- }