DV. DV. - 1 month ago 12
Java Question

Authenticating against Active Directory with Java on Linux

I have a simple task of authenticating against Active Directory using Java. Just verifying credentials and nothing else. Let's say my domain is "fun.xyz.tld", OU path is unknown, and username/password is testu/testp.

I know there's a few Java libraries out there that simplify this task, but I wasn't successful at implementing them. Most examples that I've found addressed LDAP in general, not specifically Active Directory. Issuing LDAP request means sending an OU path in it, which I don't have. Also, the application that issues LDAP request should be already bound to Active Directory in order to access it... Insecure, since the credentials would have to be stored someplace discoverable. I would like a test bind with test credentials, if possible - this would mean that account is valid.

Last, if possible, is there a way to make such authentication mechanism encrypted? I know that AD uses Kerberos, but not sure if Java's LDAP methods do.

Does anyone has an example of working code? Thanks.

DV. DV.
Answer

Here's the code I put together based on example from this blog: LINK and this source: LINK.

import com.sun.jndi.ldap.LdapCtxFactory;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Iterator;
import javax.naming.Context;
import javax.naming.AuthenticationException;
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.SearchControls;
import javax.naming.directory.SearchResult;
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE;

//import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;

class App2 {

    public static void main(String[] args) {

        if (args.length != 4 && args.length != 2) {
            System.out.println("Purpose: authenticate user against Active Directory and list group membership.");
            System.out.println("Usage: App2 <username> <password> <domain> <server>");
            System.out.println("Short usage: App2 <username> <password>");
            System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)");
            System.exit(1);
        }

        String domainName;
        String serverName;

        if (args.length == 4) {
            domainName = args[2];
            serverName = args[3];
        } else {
            domainName = "xyz.tld";
            serverName = "abc";
        }

        String username = args[0];
        String password = args[1];

        System.out
                .println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName);

        // bind by using the specified username/password
        Hashtable props = new Hashtable();
        String principalName = username + "@" + domainName;
        props.put(Context.SECURITY_PRINCIPAL, principalName);
        props.put(Context.SECURITY_CREDENTIALS, password);
        DirContext context;

        try {
            context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props);
            System.out.println("Authentication succeeded!");

            // locate this user's record
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SUBTREE_SCOPE);
            NamingEnumeration<SearchResult> renum = context.search(toDC(domainName),
                    "(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls);
            if (!renum.hasMore()) {
                System.out.println("Cannot locate user information for " + username);
                System.exit(1);
            }
            SearchResult result = renum.next();

            List<GrantedAuthority> groups = new ArrayList<GrantedAuthority>();
            Attribute memberOf = result.getAttributes().get("memberOf");
            if (memberOf != null) {// null if this user belongs to no group at all
                for (int i = 0; i < memberOf.size(); i++) {
                    Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" });
                    Attribute att = atts.get("CN");
                    groups.add(new GrantedAuthorityImpl(att.get().toString()));
                }
            }

            context.close();

            System.out.println();
            System.out.println("User belongs to: ");
            Iterator ig = groups.iterator();
            while (ig.hasNext()) {
                System.out.println("   " + ig.next().toString());
            }

        } catch (AuthenticationException a) {
            System.out.println("Authentication failed: " + a);
            System.exit(1);
        } catch (NamingException e) {
            System.out.println("Failed to bind to LDAP / get account information: " + e);
            System.exit(1);
        }
    }

    private static String toDC(String domainName) {
        StringBuilder buf = new StringBuilder();
        for (String token : domainName.split("\\.")) {
            if (token.length() == 0)
                continue; // defensive check
            if (buf.length() > 0)
                buf.append(",");
            buf.append("DC=").append(token);
        }
        return buf.toString();
    }

}