Java Authentication – LDAP and Active Directory

Been asked to integrate your application’s authentication with an LDAP directory (Active Directory is LDAP v3 compliant)? Me too! There is a fair amount of information about this topic available by searching, but when I was doing this I couldn’t find one place that had everything explained in detail, so I decided to document how I did it.

What follows will explain how to validate a username/password combination against an LDAP compliant directory server using java and the opensource LDAP library called jldap.

First off, go and download the jldap jar file and browse around the code samples as it’s a well documented library. Second, take a look how easy it is to make a connection to an LDAP directory server.

LDAPConnection conn = new LDAPConnection();
conn.connect("localhost", 389);
conn.bind("cn=admin,dc=tilion,dc=org,dc=uk", "password");

If you’re not too sure about LDAP syntax you may like to read the Wiki LDAP entry. In short, LDAP uses a tree structure where each entry has a unique identifier, known as it’s Distinguished Name (DN). In the username above cn=admin is the Relative Distinguished Name (RDN) and dc=tilion,dc=org,dc=uk is the DN of it’s parent entry. Put together these form the DN for a user with privileges to bind to the LDAP directory (DC stands for Domain Component). Entries generally have a CN attribute, known as the Common Name along with a whole load of more familiar named attributes.

If binding to an Active Directory server, the username is more likely to be of the format cn=Administrator,cn=Users,dc=tilion,dc=org,dc=uk. Part of the complexity with LDAP queries is that there is no fixed format for where particular types of entry live from server to server. Most Active Directory servers will be alike, but won’t be the same when compared to a Novell directory or an OpenLDAP server. For the purposes of authentication we need to locate where in the directory the entries that represent a user object live.

  • cn=Users,dc=tilion,dc=org,dc=uk is the default for Active Directory
  • ou=People,dc=tilion,dc=org,dc=uk is the default an OpenLDAP server storing unix users accounts (the one I have anyway!)

Whoever set up the LDAP server should be able to tell you the base DN for your environment. For example, when setting up Active Directory you specify the name (check terminology) in the format machine.domain.ext, which would lead to the base DN of dc=machine,dc=domain,dc=ext.

Here is where we get to the querk. Imagine we need to check a login where the username is darren and the password, well, lets just say it’s the right password for the username. On an OpenLDAP server, all you need to do is try binding to the directory as shown below.

conn.bind("uid=darren,ou=People,dc=tilion,dc=org,dc=uk", "password");

uid is the attribute that holds the actual username value (the CN, or Common Name is often different to the actual username).

Unfortunately, Active Directory is different, in that you can only bind to it using a DN, which references the actual entry via it’s CN. What you have to do is perform a query to check if the username exists, grab it’s CN and then perform a bind using the CN and the given password. So, let’s see this in action …

// assume we have a connection, which is already bound
LDAPSearchResults searchResults = conn.search(
        "cn=Users,dc=tilion,dc=org,dc=uk",
        LDAPConnection.SCOPE_ONE,
        sAMAccountName + "=" + <username>,  // <username> came from the user trying to login
        null,
        false);
LDAPEntry entry = searchResults.next();
if (entry != null) {  // the username is valid, lets pull out the CN from the attributes
    String cnValue = null;
    LDAPAttributeSet attrSet = entry.getAttributeSet();
    Iterator allAttrs = attrSet.iterator();
    while (allAttrs.hasNext()) {
        LDAPAttribute attr = (LDAPAttribute)allAttrs.next();
        String attrName = attr.getName();
        if (attrName.equalsIgnoreCase("cn")) {  // we got the CN
            cnValue = attr.getStringValues().nextElement();
        } else {
            continue;
        }
    }

    if (cnValue == null) {
        // return auth failed, the username doesn't exist
    }

    // attempt a bind with CN and given password
    LDAPConnection tmp = new LDAPConnection();
    tmp.connect(HOST, PORT);
    tmp.bind("cn=" + cnValue + "," + "cn=Users,dc=tilion,dc=org,dc=uk", <password>);  // <password> came from the user trying to login

    // return auth successful, username and password are valid

    // an LDAPException is thrown if the credentials are invalid
}

Concepts covered, you’re probably wondering how are you going to find all those cn,dn,dc,xyz details about your particular LDAP directory? That’s exactly why I created a standalone application to query an LDAP server when I was learning this stuff. You can download the LDAP test application (NOT UPLOADED YET!), which includes the the compiled jar, full source and a maven pom.xml.

The code shown here is for illustration purposes only and should not be used in production without proper error handling additions. It is as concise as possible to illustrate a point.

Useful Attributes

A quick round up of useful attributes in various LDAP compliant servers.

Active Directory

  • sAMAccountName holds the username
  • displayName holds the full name
  • mail holds the email address

OpenLDAP (holding unix user accounts)

  • uid holds the username
  • cn holds the full name
  • mailacceptinggeneralid holds the email address

11 thoughts on “Java Authentication – LDAP and Active Directory

  1. I went to site you linked looking for the jar you mention and they don’t have it. Do you have a link to the actual jar or must I build their source code. thanks

    • You have to store the password somewhere, so it’s no worse than storing it in a plaintext config file. Having to recompile your code to change the password isn’t ideal though.

      The most important thing is that you don’t bind as the admin user. Create a new user account who’s only privilege is to bind and search, then even if the password is discovered, the user cannot edit LDAP/AD. When making your new user account, remember to set the password to never expire.

  2. I tried using the above code to bind my cn for username-password validation check when authentication is successfully done. But I getting the following exception :
    [LDAP: error code 17 – javaSerializedData: attribute type undefined]; remaining name ‘cn=Himanshu Patidar,ou=people,dc=example,dc=com’

    • Double check your distinguished name is correct as it doesn’t look like AD (your LDAP server may not verify for you).
      cn=Himanshu Patidar,ou=people,dc=example,dc=com

      Afraid I’ve not seen this error before, so if that’s not the problem, google is your best bet.

  3. Pingback: Secure Java Authentication (SSL) to Active Directory | Tilion

  4. Thank you! A while since you posted this, but easily the best doco on this topic, I was going around in circles before coming here!

Leave a Reply

Your email address will not be published. Required fields are marked *

Security Question * Time limit is exhausted. Please reload the CAPTCHA.