2007/10/5

Using ldap api to connect MS Active Directory

當系統需要透過外部的帳號倉庫做帳號認證時,我們以Spring外接一段程式,處理系統登入與修改密碼的程序,連接外部的LDAP或AD。

當我遇到MS AD時,我必須要先把他安裝起來,這是我遇到的第一個困難,我參考了JNDI AD 備忘記的程序,先把MS Windows 2003標準版安裝在VM上,然後設定了AD,接下來設定了IIS,並安裝CA,把SSL搞定跟key store搞定,其中我以ldap browser測試過,可以連接AD成功,瀏覽整個AD Tree的資料。

後來就參考了java如何結合(呼叫)Win2000 ActiveDirectory,做出了AD認證的程式,這個認證有點奇怪,因為只要是合法的帳號,就可以瀏覽所有的使用者節點資料。

接下來就是修改密碼,參考如何使用 JNDI change win 2000 AD user password,可以處理修改密碼的程序,但前提是MS IIS export的key必須要先import到JVM的cacerts這個key store裡面,得用SSL才能運作。

搜尋所有使用者帳號的查詢,就是透過ldap的searchFilter功能,針對Users,把一些非一般使用者帳號的條件放進去,將不合用的帳號過濾掉,拉出資料的查詢結果,就完成了(查詢結果回傳的ADUser物件,只是一個包含了id與name欄位的data bean物件)。

安裝是最麻煩的地方......。

public class ADClient {
private static Logger logger = Logger.getLogger(ADClient.class.getName());

public static boolean checkAuth(String ldapserver, String userdn, String password)
throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// env.put(Context.PROVIDER_URL, "ldap://192.168.2.200");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=" + uid + ",CN=Users,DC=msdiv,DC=com,DC=tw");
env.put(Context.SECURITY_PRINCIPAL, userdn);
env.put(Context.SECURITY_CREDENTIALS, password);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;
} catch (javax.naming.AuthenticationException authe) {
logger.error("Error: ", authe);
bLogin = false;
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return bLogin;
}

public static void changePwd(String ldapserver, String admindn, String adminpassword,
String userdn, String newPassword, String keystorePath, String keystorePassword)
throws NamingException, UnsupportedEncodingException {
Hashtable env = new Hashtable();
// String admindn = "cn=administrator,CN=Users,DC=msdiv,DC=com,DC=tw";
// String adminpassword = "adtest1111";
// String userdn = "CN=test,CN=Users,DC=msdiv,DC=com,DC=tw";
// String newPassword = "test2222";

// System.out.println("java.home=" + System.getProperty("java.home"));
// String keystore = System.getProperty("java.home") + "\\lib\\security\\cacerts";
// String keystore = "C:\\Program Files\\Java\\jdk1.6.0\\jre\\lib\\security\\cacerts";

System.setProperty("javax.net.ssl.trustStore", keystorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePassword);
System.setProperty("UseSunHttpHandler", "true");

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);
env.put(Context.SECURITY_PROTOCOL, "ssl");
// String ldapURL = "ldaps://192.168.2.200";
env.put(Context.PROVIDER_URL, ldapserver);

LdapContext ctx = null;
try {
logger.info("AD is trying to reset Password for: " + userdn);
ctx = new InitialLdapContext(env, null);
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
"unicodePwd", newUnicodePassword));
ctx.modifyAttributes(userdn, mods);
logger.info("AD Reset Password for: " + userdn + " ok.");
} finally {
if (ctx != null) {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
}

}

public static ArrayList queryAD(String ldapserver, String admindn,
String adminpassword, int searchScope, String searchBaseDn, String searchFilter) throws NamingException {
ArrayList adusers=new ArrayList();

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=Administrator,CN=Users,DC=msdiv,DC=com,DC=tw");
// env.put(Context.SECURITY_CREDENTIALS, "adtest1111");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;

// Create the search controls
SearchControls searchCtls = new SearchControls();

// Specify the search scope
// searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// searchCtls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
searchCtls.setSearchScope(searchScope);

// specify the LDAP search filter
// String searchFilter = "(& (objectClass=user) (!(adminCount=*))
// (!(isCriticalSystemObject=*)) (userPrincipalName=*) )";

// Specify the Base for the search
// String searchBaseDn = "CN=Users,DC=msdiv,DC=com,DC=tw";

// initialize counter to total the group members
int totalResults = 0;

// Specify the attributes to return
// String returnedAtts[] = { "sAMAccountName", "givenName", "mail" };
String returnedAtts[] = { "sAMAccountName", "name", "userPrincipalName" };
searchCtls.setReturningAttributes(returnedAtts);

// Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBaseDn, searchFilter, searchCtls);

// Loop through the search results

String id = "";
String name = "";
while (answer.hasMoreElements()) {
totalResults++;
SearchResult sr = (SearchResult) answer.next();

logger.info(totalResults + ">" + sr.getName());

// Print out the groups

Attributes attrs = sr.getAttributes();
if (attrs != null) {

try {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
logger.debug(" Attribute: " + attr.getID());
if (attr.getID().equals("sAMAccountName")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
id = (String) e.next();
logger.debug(" value: " + id);
}
} else if (attr.getID().equals("name")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
name = (String) e.next();
logger.debug(" value: " + name);
}
} else {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
logger.debug(" value: " + e.next());
}
}
}
} catch (NamingException e) {
logger.error("Can not retrieve data from AD!");
logger.error("Error: ", e);
throw e;
}
}

if( !id.equals("")) {
adusers.add(new ADUser(id, name));
}
}
logger.info("Total results: " + totalResults);
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return adusers;
}
}