參考來源:
ActiveDirectoryAuthentication
原作者是使用ldap,下面修改為ldaps驗證
為了安全考量,有一份堅持,
原作者是用nsLookup去取得AD的主機列表,
下面是先hardcode加上一台AD主機位址,並且加上憑證
當然憑證要匯到JDK的ca路徑
範例網域:contoso.com,當然要換成你要用的Domain
package com.uitox.shared.util;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.naming.CommunicationException;
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.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.login.AccountException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
public class ActiveDirectoryAuthentication {
private static final String CONTEXT_FACTORY_CLASS = "com.sun.jndi.ldap.LdapCtxFactory";
private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};
private int lastLdapUrlIndex;
private final String domainName;
public ActiveDirectoryAuthentication(String domainName) {
this.domainName = domainName.toUpperCase();
try {
// ldapServerUrls = nsLookup(domainName);
} catch (Exception e) {
e.printStackTrace();
}
lastLdapUrlIndex = 0;
}
public boolean authenticate(String username, String password)
throws LoginException {
if (ldapServerUrls == null || ldapServerUrls.length == 0) {
throw new AccountException("Unable to find ldap servers");
}
if (username == null || password == null
|| username.trim().length() == 0
|| password.trim().length() == 0) {
throw new FailedLoginException("Username or password is empty");
}
int retryCount = 0;
int currentLdapUrlIndex = lastLdapUrlIndex;
do {
retryCount++;
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY_CLASS);
env.put(Context.PROVIDER_URL,
ldapServerUrls[currentLdapUrlIndex]);
env.put(Context.SECURITY_PROTOCOL, "ssl");
System.setProperty("javax.net.ssl.trustStore","/jre/lib/security/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword","changeit");
env.put(Context.SECURITY_PRINCIPAL, username + "@" + domainName);
env.put(Context.SECURITY_CREDENTIALS, password);
DirContext ctx = new InitialDirContext(env);
ctx.close();
lastLdapUrlIndex = currentLdapUrlIndex;
return true;
} catch (CommunicationException exp) {
// TODO you can replace with log4j or slf4j API
exp.printStackTrace();
// if the exception of type communication we can assume the AD
// is not reachable hence retry can be attempted with next
// available AD
if (retryCount < ldapServerUrls.length) {
currentLdapUrlIndex++;
if (currentLdapUrlIndex == ldapServerUrls.length) {
currentLdapUrlIndex = 0;
}
continue;
}
return false;
} catch (Throwable throwable) {
throwable.printStackTrace();
return false;
}
} while (true);
}
private static String[] nsLookup(String argDomain) throws Exception {
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.dns.DnsContextFactory");
env.put("java.naming.provider.url", "dns:");
DirContext ctx = new InitialDirContext(env);
Attributes attributes = ctx.getAttributes(
String.format("_ldap._tcp.%s", argDomain),
new String[] { "srv" });
// try thrice to get the KDC servers before throwing error
for (int i = 0; i < 3; i++) {
Attribute a = attributes.get("srv");
if (a != null) {
List domainServers = new ArrayList();
NamingEnumeration enumeration = a.getAll();
while (enumeration.hasMoreElements()) {
String srvAttr = (String) enumeration.next();
// the value are in space separated 0) priority 1)
// weight 2) port 3) server
String values[] = srvAttr.toString().split(" ");
domainServers.add(String.format("ldap://%s:%s",
values[3], values[2]));
}
String domainServersArray[] = new String[domainServers
.size()];
domainServers.toArray(domainServersArray);
return domainServersArray;
}
}
throw new Exception("Unable to find srv attribute for the domain "
+ argDomain);
} catch (NamingException exp) {
throw new Exception("Error while performing nslookup. Root Cause: "
+ exp.getMessage(), exp)
}
}
}
主要改造說明
ldapServerUrls直接加入主機
22行處改成,加上DC主機,ex:ldap://fdc.contoso.com:636
private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};
註解掉原nsLookup查表,因為我hardcode了 XD
指定憑證CA檔
先將企業內部root CA匯入,至/jre/lib/security/cacerts
然後指定CA檔,讓java知道就行了
System.setProperty("javax.net.ssl.trustStore","<JDK ROOT>/jre/lib/security/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword","changeit");
最後,在寫一個驗證的Class,作登錄就大功告成。
public class UserCredentialManager {
...
public synchronized void login(String name, String password){
boolean IsAuth = false;
ActiveDirectoryAuthentication ADAuth = new ActiveDirectoryAuthentication("CONTOSO.COM");
try {
IsAuth = ADAuth.authenticate(name, password);
} catch (LoginException e) {
e.printStackTrace();
}
if(IsAuth == true){
user = name;
}
}
...
}