Authenticating users with Active Directory

PassportA very common requirement for intranet and extranet applications is the need to authenticate users against an Active Directory (or LDAP) database. The Java Extension in this article describes how to do exactly that.

This article describes a simple Java Extension that manages the HTTP Basic Authentication process and validates the supplied username and password against an Active Directory database. It shows how to use Initialization Parameters to provide configuration to an extension, and how authentication results can be cached to reduce the load on the Active Directory server:

Using Active Directory

Step 1: The Java Extension

Use Eclipse or an alternative Java IDE to build an extension named 'LdapAuthenticate' from the following code:

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zeus.ZXTMServlet.ZXTMHttpServletRequest;


public class LdapAuthenticate extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private String dirServer;
    private String realm;
    
    public void init( ServletConfig config) throws ServletException {
        super.init( config );
        dirServer = config.getInitParameter( "DB" );
        realm = config.getInitParameter( "Realm" );
        if( dirServer == null ) throw new ServletException( "No DB configured" );
        if( realm == null ) realm = "Secure site";
    }
    
    public void doGet( HttpServletRequest req, HttpServletResponse res )
        throws ServletException, IOException
    {
        try {
            ZXTMHttpServletRequest zreq = (ZXTMHttpServletRequest)req;
            
            String[] userPass = zreq.getRemoteUserAndPassword();
            if( userPass == null ) throw new Exception( "No Authentication details" );

            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put( Context.PROVIDER_URL, "LDAP://" + dirServer );
            env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
            env.put( Context.SECURITY_PRINCIPAL, userPass[0] );
            env.put( Context.SECURITY_CREDENTIALS, userPass[1] );

            DirContext ctx = new InitialDirContext( env );
            ctx.close();
            
            // No exceptions thrown... must have been successful ;-)
            return;
        } catch( Exception e ) {
            res.setHeader( "WWW-Authenticate", "Basic realm=\"" + realm + "\"" );
            res.setHeader( "Content-Type", "text/html" );
            res.setStatus( 401 );
            
            String message = 
                "<html>" +
                "<head><title>Unauthorized</title></head>" +
                "<body>" +
                "<h2>Unauthorized - please log in</h2>" +
                "<p>Please log in with your system username and password</p>" +
                "<p>Error: " + e.toString() + "</p>" +
                "</body>" +
                "</html>";
            
            PrintWriter out = res.getWriter();
            out.println( message );
        }
    }

    public void doPost( HttpServletRequest req, HttpServletResponse res )
        throws ServletException, IOException
    {
        doGet( req, res );
    }
}

Alternatively, download the compiled 'LdapAuthenticate' extension.

For more information on building Java Extensions, see the "Watermarking Images with Java Extensions" article, and the ZXTM 5.0 Java Development Guide.

Configuring the Java Extension

Upload the LdapAuthenticate Java Extension using the Catalog -> Java page. Click on the LdapAuthenticate link to edit the properties of the extension, and add two Initialization Parameters:

Init Parameters

DB specifies the name of the LDAP or Active Directory database, and Realm specifies the authentication realm.

These parameters are read when the extension is initialized, which occurs the first time the extension is used by ZXTM:

   public void init( ServletConfig config) throws ServletException {
        super.init( config );
        dirServer = config.getInitParameter( "DB" );
        realm = config.getInitParameter( "Realm" );
        if( dirServer == null ) throw new ServletException( "No DB configured" );
        if( realm == null ) realm = "Secure site";
    }

If you change the value of one of these parameters, use the 'Force Reload' option in the ZXTM Admin Server to unload and reload this extension.

Either use the auto-generated rule, or create a new TrafficScript rule to call the extension on every request to an HTTP virtual server:

java.run( "LdapAuthenticate" );

Testing the Java Extension

When you try to access the web site through ZXTM, you will be prompted for a username and password; the LdapAuthenticate extension checks that the username and password can bind to the configured Active Directory database, and refuses access if not:

If you are unable to log in, cancel the prompt dialog box to see the error reported by the Java extension. In the following case, the extension was misconfigured; it could not resolve the name of the database server provided in the 'DB' parameter:

Step 2: Caching the Authentication Results

Caching the Authentication response from the Java extension will improve the performance of the web site and reduce the load on the database server.

You can modify the TrafficScript rule that calls the extension so that it records successful logins, caching them for a period of time. The following rule uses the data.set() TrafficScript function to record successful logins, caching this information for 10 minutes before attempting to reauthenticate the user against the database server.

$auth = http.getHeader( "Authorization" );

if( data.get( $auth ) < sys.time() ) {
   data.remove( $auth );
   java.run( "LdapAuthenticate" );

   # if we got here, we were authenticated.
   # Cache this information for 600 seconds
   data.set( $auth, sys.time()+600 );
}

Conclusion

This example shows how you can apply a consistent access policy across your application infrastructure, even if your service is made up of several different types of back-end applications, some of which may not support your desired authentication method.

Owen Garrett [Zeus Dev Team] 22 May 2008  Permalink  
Leave a comment ...
Your email address will not be displayed.
Your URL will be displayed.
This public messageboard is not a forum for technical support. To report technical support problems, please contact our dedicated Support team using the instructions at the bottom of this page.
Options:
 
(Line breaks become <br />)
(Set cookies for name, email & url)
Download Free ZXTM Desktop Edition

Recent Articles

Other Resources



www.zeus.com