LDAPAuthentication with Active Directory

Here’s something I had to piece together myself from various code fragments. Its a class to authenticate a user in Windows Active Directory environment using LDAP. It first locates the domain controllers (DNS lookup of SRV records for _ldap._tcp.domain), parses out the server part and then tries to authenticate the user against a domain controller.

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
 
import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.Context;
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 com.sun.jndi.ldap.LdapCtxFactory;
 
/**
 * LDAPAuthentication class for authenticating Microsoft Active Directory users
 * 
 * If the user or password is wrong, you'll get an AuthenticationException If
 * none of the domain controllers are reachable, you'll get a
 * CommunicationException. If a domain controller cannot be located (via DNS)
 * you'll get a NamingException.
 * 
 * @author Roger Armstrong, Armstrong Consulting GmbH
 * 
 */
public class LDAPAuthentication {
	public static void authenticateUser(String user, String password, String domain) throws AuthenticationException, NamingException {
		List<String> ldapServers = findLDAPServersInWindowsDomain(domain);
		if (ldapServers.isEmpty())
			throw new NamingException("Can't locate an LDAP server (try nslookup type=SRV _ldap._tcp." + domain + ")");
 
		Hashtable<String, String> props = new Hashtable<String, String>();
		String principalName = user + "@" + domain;
		props.put(Context.SECURITY_PRINCIPAL, principalName);
		props.put(Context.SECURITY_CREDENTIALS, password);
		Integer count = 0;
		for (String ldapServer : ldapServers) {
			try {
				count++;
				LdapCtxFactory.getLdapCtxInstance("ldap://" + ldapServer, props);
				return;
			} catch (CommunicationException e) { // this is what'll happen if one of the domain controllers is unreachable
				if (count.equals(ldapServers.size())) {
					// we've got no more servers to try, so throw the CommunicationException to indicate that we failed to reach an LDAP server
					throw e;
				}
			}
		}
	}
 
	private static List<String> findLDAPServersInWindowsDomain(String domain) throws NamingException {
		List<String> servers = new ArrayList<String>();
		Hashtable<String, String> env = new Hashtable<String, String>();
		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("_ldap._tcp." + domain, new String[] { "SRV" }); // that's how Windows domain controllers are registered in DNS
		Attribute a = attributes.get("SRV");
		for (int i = 0; i < a.size(); i++) {
			String srvRecord = a.get(i).toString();
			// each SRV record is in the format "0 100 389 dc1.company.com."
			// priority weight port server (space separated)
			servers.add(srvRecord.split(" ")[3]);
		}
		ctx.close();
		return servers;
	}
}

Leave a Reply


WordPress Appliance - Powered by TurnKey Linux