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\User
String adminPassword = "12345678"; // 密码
HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); // LDAP访问安全级别
HashEnv.put(Context.SECURITY_PRINCIPAL, adminName); // AD User
HashEnv.put(Context.SECURITY_CREDENTIALS, adminPassword); // AD Password
HashEnv.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
// 初始化搜索结果数为0
int totalResults = 0;// Specify the attributes to return
int rows = 0;
while (answer.hasMoreElements()) {// 遍历结果集
SearchResult sr = (SearchResult) answer.next();// 得到符合搜索条件的DN
System.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
}
}// while
System.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的基本代码:
- 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结构未知,所以查询仍然要保守点。
- 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) 时间的处理
- 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();
- }
来源:oschina
链接:https://my.oschina.net/u/1583179/blog/682443