Tomcat and OpenLDAP, from Configuration to Application Blog

Version 2



    What Is LDAP?
    OpenLDAP Setu p and Configuration
    JXplorer: Visual LDAP
    Tomcat Configuration for OpenLDAP
    Tomcat Server Configuration
    Web Application Configuration
    Jakarta TagLibs

    Almost all Java web applications require some type of secured access, and this is usually addressed via a Lightweight Directory Access Protocol (LDAP) directory. As a developer, it is advantageous to have a local OpenLDAP directory and web container to enhance productivity. Configuring Tomcat to connect to an OpenLDAP directory is a relatively straightforwardif the process is understood. This article details that process, and examines the Apache Jakarta project tag library (taglib) to demonstrate an easy way to test the security mechanisms.

    What Is LDAP?

    LDAP is many things to many people. For the purposes of this article, it is simply a common protocol to allow a user to be authenticated via a user identifier and password. In other words, it provides authentication. In addition, it also allows a user to be authorized for access to specific application areas. This is an authorization process. There is a clear distinction between authentication and authorization. They arenot the same thing.

    LDAP directories are, in theory, interchangeable. However, there are configuration changes to be taken into account. An LDAP directory is organized into a tree hierarchy consisting of some of the following levels:

    • The Root (the source of the tree), which branches out to
    • Countries, each of which branches out to
    • Organizations, which branch out to
    • Organizational units (divisions, departments, areas, etc)., which branches out to (finally)
    • Individuals.

    LDAP directories can import and export their contents via the Lightweight Directory Interchange Format (LDIF) file format. LDIF is a text file format that can be used to exchange information between LDAP directories, or for this case, to initially configure a directory. Since it is open source, OpenLDAP is the directory server of choice for many developers.

    OpenLDAP Setup and Configuration

    After downloading and installing OpenLDAP, the slapd.conffile in the installation folder requires some rather important changes.

    Begin by including the InetOrgPerson schema. This schema has many useful attributes predefined for network users, including the uid attribute for a specific user's logon ID. TheInetOrgPerson schema has a dependency on the Cosineschema, hence its inclusion.

    ucdata-path C:/openldap/ucdata include C:/openldap/etc/schema/core.schema include C:/openldap/etc/schema/cosine.schema include C:/openldap/etc/schema/inetorgperson.schema 
    ......... snip ....... database bdb suffix dc="mycompany",dc="com" rootdn "cn=Manager,dc=mycompany,dc=com" rootpw secret directory C:/openldap/var/openldap-data index objectClass eq 

    The second set of changes tells OpenLDAP which type of database to use and what the root distinguished name and password are. These are the keys to the city, so choose them as you would any other password.

    Once all changes have been made to the slapd.conf file, it is time to start OpenLDAP. Go to a command prompt and change directory to the install folder (c:\openldap by default). Issue the command .\slapd -d 1 and the server should start much like Figure 1.


    Figure 1
    Figure 1. OpenLDAP startup
    (click for full-size image)

    If the server issues an error and fails to start, check that the schema entries (above) have been added to the slapd.conffile. Also, if OpenLDAP was not installed in its default location, verify that the paths are correct for this location. Once the server is running, we can verify the installation. Open another command prompt and issue the command ldapsearch -x -b "dc=mycompany,dc=com" "(objectclass=*)". The return should look much like Figure 2.


    Figure 2
    Figure 2. OpenLDAP is working
    (click for full-size image)

    To populate the directory, a LDIF file will be imported into it. The format is very unforgiving, so there is an LDIF file available for download. To import the file, use a command prompt to executeldapadd -x -D "cn=Manager,dc=mycompany,dc=com" -W -f setup.ldif and you should be prompted for your password, as configured in the slapd.conf file. Once the password is entered, the results should be as they are in Figure 3.


    Figure 3
    Figure 3. ldapadd in action
    (click for full-size image)

    That was all very nice, but what did these actions accomplish? Well, basically ldapadd added two organizational units (people and roles), three user roles (under the roles OU) and three users (under the people OU). These users were also assigned to different roles. The hierarchy that is created by importing the LDIF import follows:

    • com
      • mycompany
        • people
          • admin
          • jbloggs
          • sspecial
        • roles
          • Admin Users
            • uid=admin/ou=people/...
          • Special Users
            • uid=sspecial/ou=people/...
          • Test Users
            • uid=sspecial/ou=people/...
            • uid=jbloggs/ou=people/...
            • uid=admin/ou=people/...

    You can now issue another ldapsearch -x -b "dc=mycompany,dc=com" "(objectclass=*)", and this time the output should contain something more meaningful (like user attributes and role memberships), as in Figure 4. While all of this command-line typing is adequate, a rather good open source Java Swing LDAP browser named JXplorer can help.


    Figure 4
    Figure 4. ldapsearch returns users and roles (click for full-size image)

    JXplorer: Visual LDAP

    JXploreris a LDAP browser. It can be used to view, add, change, and delete any of the elements or attributes in the directory. It can even be used to change passwords, if necessary. Install JXplorer and runjxplorer.bat or, depending upon your operating system. Once it's running, connect to your local OpenLDAP directory using your root user and password, as shown in Figure 5.

    JXplorer connection screen
    Figure 5. JXplorer connection screen

    JXplorer can be used to browse the directory and change any values. Be careful when changing anything but user attributes (email, password, etc.) until you are comfortable with LDAP. See Figure 6 for an example.


    Figure 6
    Figure 6. JXplorer browsing the LDAP directory (click for full-size image)

    Tomcat Configuration for OpenLDAP

    The Servlet Specification v2.3 allows an application server to use container-managed security to connect to an existing user repository for the purposes of authentication and authorization. There is not, however, a standard way to accomplish this among different containers. Each container can (and usually does) implement this in different way. The Tomcat container uses the notion of a Realm for this type of security. A Realm can be described as a "repository" of user names and passwords that uniquely identifies valid users of a web application. There are four realm types in the Tomcat 4 container and each type stores and retrieves user data and passwords in a different type of repository.

    • JDBCRealm uses a relational database.
    • DataSourceRealm uses a relational database via a JNDI connection.
    • JNDIRealm uses a JNDI connection, usually a LDAP directory.
    • MemoryRealm uses an in-memory file, usuallytomcat-users.xml.

    Tomcat's default Realm is aMemoryRealm, which is configured via thetomcat-users.xml file. Tomcat needs to be configured for aJNDIRealm to allow authentication via a LDAP directory.

    There are several steps needed to configure Tomcat to use theJNDIRealm.

    • Set up the realm in the server.xml file.
    • Create a JSP login form.
    • Set up any <security-constraint>s to protect resources in the web.xml file.
    • Set up the <login-config> to use the login form in the web.xml file.
    • Set up all of the <security-role> mappings in the web.xml file.

    Tomcat Server Configuration

    Each application can have the Realm set up inside of its respective <Context> element; the realm is available only to that application. However, the realm can also be set up at the <Engine> or<Host> levels. Each of these has an impact on the behavior, or scope, of the realm. This allows easy sharing of a single realm over several applications.

    Here is a server.xml realm that will allow an application to connect to OpenLDAP.

    <Realm className="org.apache.catalina.realm.JNDIRealm" debug="99" connectionName="cn=Manager,dc=mycompany,dc=com" connectionPassword="secret" connectionURL="ldap://localhost:389" roleBase="ou=roles,dc=mycompany,dc=com" roleName="cn" roleSearch="(uniqueMember={0})" roleSubtree="false" userSearch="(uid={0})" userPassword="userPassword" userPattern="uid={0},ou=people,dc=mycompany,dc=com" /> 

    The userSearch and userPatternattribute values are the main sources of confusion and errors when configuring LDAP declaratively. Pay particular attention to these attributes if you are using anything other than OpenLDAP.

    Web Application Configuration

    To complete the OpenLDAP configuration for Tomcat, the application's web.xml file must be updated. The application available for download consists of six JSP pages, three of which are protected for the various roles set up in the LDAP directory. The application must be configured to allow form-based authentication and the application must be told what roles exist. First, create a login.jsp file. When using form-based authentication, this JSP must contain the following.

    <body> <form method="POST" action="j_security_check"> <input type="text" name="j_username"> <br> <input type="password" name="j_password"> <br> <input type="submit"> </form> </body> 

    The web.xml file needs to be updated to reference the new OpenLDAP realm and to use the roles designated by it. The application needs to allow public access to the login.jsppage; otherwise, no user could ever log in.

    To inform the application which resources these roles are allowed to access, URL mappings in the application (or security constraints) are used. These mappings can be either a file name (/admin.jsp) or a path (/jsp/* will protect everything in the jsp folder). The following XML indicates that both of the listed .jsp files are not to be protected.

    <security-constraint> <web-resource-collection> <web-resource-name>Public Area</web-resource-name> <!-- Define the context-relative URL(s) to be protected --> <url-pattern>/index.jsp</url-pattern> <url-pattern>/login.jsp</url-pattern> </web-resource-collection> </security-constraint> 

    Why does this code mark the .jsps as unprotected? This is due to something that is missing, rather than something that is present. The web.xml snippet below shows that theuser.jsp resource is protected, and which roles can access it.

    <security-constraint> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <!-- Define the context-relative URL(s) to be protected --> <url-pattern>/user.jsp</url-pattern> </web-resource-collection> <auth-constraint> <!-- Anyone with one of the listed roles may access this area --> <role-name>Test Users</role-name> <role-name>Special Users</role-name> <role-name>Admin Users</role-name> </auth-constraint> </security-constraint> 

    Notice that the protected resource has another section,<auth-constraint>, that specifies which application roles can access the resource. If an<auth-constraint> is present, then the resource(s) are secured. If not, they are public. Unless the entire application is protected (i.e.,<url-pattern>/</url-pattern>), the public security constraint is redundant.

    To configure the web.xml file to utilize thelogin.jsp created earlier, add the following to theweb.xml file.

    <!-- uses form-based authentication --> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.jsp</form-login-page> <form-error-page>/fail_login.html</form-error-page> </form-login-config> </login-config> 

    There is one additional step left to make in web.xml: the application needs to be informed of the roles that we are using.

    <!-- Security roles referenced by this web application --> <security-role> <role-name>Test Users</role-name> </security-role> <security-role> <role-name>Special Users</role-name> </security-role> <security-role> <role-name>Admin Users</role-name> </security-role> 

    Tomcat has now been configured to use OpenLDAP and our application has been set up correctly in the web.xml file. The first time a user navigates to a resource listed in any<security-constraint> (other than public resources) ,the server will automatically display thelogin.jsp for the user to authenticate. If the user fails to authenticate successfully, the page specified in the<form-error-page> will be displayed. Should a user be successfully authenticated, but is not authorizedfor access to the resource (due to a lack of role membership, for example), the server returns a 403 error page. Error pages can be customized if so desired in the web.xml file, using the <error-code> element.

    Note that, the web.xml has a specific order for the elements (defined by a DTD), so you should take a look at the complete web.xml available in the sample code (in the Resources section below). This is especially true of the <login-config> and<security-role> sections.

    A user can now log in, but what about a user logging out? Code could be written to invalidate the users' session, or we could use the handy session taglib supplied by the Apache Jakarta people.

    Jakarta TagLibs

    By adding a logout.jsp to the application and using thesession taglib for the Apache Jakarta Project, we can invalidate our user without the need from any custom code (in a real-world application, you may need to do some further clean up of the user's session, so this may not suffice).

    <body> <sess:invalidate/> You are now logged out<br> <a href="index.jsp">Return to index</a> </body> 

    Once the JSP page with the <sess:invalidate/>tag is displayed, the user session is removed and the user is effectively logged out. Simply putting a link to thelogout.jsp page, and having a user navigate to it, is sufficient for this simple application. In addition to the session taglib, we can also utilize the request taglib to customize the content of the JSP based on the user role or roles.

    <req:isUserInRole role="Admin Users"> The remote user is in role "Admin Users".<br /> </req:isUserInRole> 

    The preceding JSP snippet will only display if the user is in the required role. To verify the behavior of the role security, JXplorer can be used to quickly add and remove users from roles.


    Having a LDAP directory available on a local workstation can be a valuable resource for a developer. No more asking the network administrators for access, or more likely, to create test accounts for you. All of these tasks can be done in seconds instead of days or weeks. That isn't to say LDAP is simple. It isn't; it is a powerful and complex system. But with a little know-how and the right tools, it is fairly straightforward to set up.

    Thanks to the cross-platform nature of both Tomcat and OpenLDAP, the continuous build servers, CruiseControl, Anthill, etc., can be kept completely separate from any production systems. This type of decoupling has advantages for all.

    Worth noting is the fact that all LDAP servers are not equal. For instance, OpenLDAP does not support the memberOfattribute for role membership checking, which can be very irritating at times. The OpenLDAP realm for Tomcat will not necessarily work if you change Directory servers and the associated values. That is probably the single biggest challenge when using LDAP, but by using ldapsearch you can eventually figure out the values needed.