How JAAS enables use of custom security repositories with J2EE applications

Java Development News:

How JAAS enables use of custom security repositories with J2EE applications

By Pramati

01 Jan 2000 | TheServerSide.com

This tutorial describes how a developer can write a custom JAAS LoginModule for using an LDAP authentication data store along with a Java 2 Platform, Enterprise Edition (J2EE) application.

The tutorial includes a sample implementation of an LDAP based LoginModule which is downloadable as jaastutorial.zip.


Why JAAS in J2EE

One of the limitations of the J2EE version 1.2 platform was that it did not provide application developers with a standard route for integrating the application server realm with existing or custom security infrastructures. J2EE version 1.3 now solves that with the inclusion of the Java Authentication and Authorization Service (JAAS) framework.  Read more on JAAS here.

J2EE application servers that implement JAAS provide enterprise application developers with the standard Login Module API for tapping custom or legacy security systems from their applications. While application developers write to the LoginModule API (specifically, the LoginContext API), the application server implements the LoginModule interface.

The standards-based LoginModule interface gives J2EE developers the freedom to tap a variety of information sources that use Java Database Connectivity, the lightweight directory access protocol (LDAP), or Shared File Systems to store authentication data , without requiring them to modify the application code. Indeed, there are an increasing number of scenarios where J2EE application developers wish to tap custom authentication repositories from their applications. They would do this by writing a Login Module, packaging it along with their application, and distributing it to target J2EE application servers in a prescribed way.


The J2EE Model: Roles, Users and JAAS

The J2EE model defines security at two levels: the system level and the application level. System level security is defined in terms of User Groups, called Roles, and in terms of security privileges mapping definitions, called Realms. Realms are mappings of one or more User Groups to a set of privileges or permissions.

Application level security is constituted from User Groups and Realms. At the application level, security permissions also list the various application components that are accessible by each User Group in each Realm. Thus, when an application is deployed, its application level realms and roles are mapped to the system level realms and roles defined on the server.

J2EE application servers implementing JAAS enable application developers to write a custom "pluggable" login module in the server environment. Such a module provides a conduit for roles defined in the packaged application to user group information stored in some custom authentication repository, such as an LDAP server.



How a LoginModule helps application roles and groups map to authentication data stored in a custom repository such as LDAP.




How JAAS Integrates with the App Server

The constituents of the JAAS solution are

  • LoginModule

  • Application Server's Security Service

  • J2EE Application

The following interaction diagram depicts an overview of the interaction among these constituents.






Writing the JAAS Security Module

A J2EE application developer writing security with JAAS would basically write the Login Module, the JAAS interface implementation that holds the authentication logic. Application servers typically ship with standard Login Module implementations. Application developers may want to write their own implementation and will see how to do this tl through the following steps:

  1. Writing the LoginModule interface (LoginContext API)

  2. Writing the CallBackHandler interface that enables client to pass authentication data to the server.

  3. Configuring the LoginModule and CallBackHandler with the server and application.

  4. Packaging the application along with module classes

  5. Integrating the LoginModule with the application server


Step 1: Writing the LoginModule

In this tutorial, you will see code snippets from a LoginModule implementation for an LDAP Server. We also demonstrate how to test the LDAP LoginModule sample in a typical J2EE application server environment.

This is how the LoginModule implementation class is defined:


 

 public class LDAPLoginModule implements LoginModule


The standard JAAS packages required by this class are imported as shown here:


 

 import javax.security.*;


Standard methods in the LoginModule that must be implemented are:


  1. initialize()

  2. login()

  3. commit()

  4. abort()

  5. logout()


initialize()

The initialize method does the following:

  1. Sets configurations required by the LoginModule

  2. Collects login information that is encapsulated in the CallBackHandler

  3. Initializes and instantiates all configuration parameters for this instance of the LoginModule

The client instantiates the LoginContext object and passes a CallBackHandler instance with the user name and password. When the LoginContext object is instantiated, the initialize() method of the LoginModule is triggered.


 public static void main(String args[]) { LoginContext lc = new LoginContext("Login", new MyCallbackHandler(args[0],args[1])); }

login()

This method returns a boolean variable, which is true if the authentication information provided is valid. The login method performs the following tasks:


  1. Fetches the login information

  2. Authenticates the user


The login information is fetched using the CallBackHandler. The code that does this is shown here:

  Callback[] calls=new Callback[2]; calls[0]=new NameCallback("name"); calls[1]=new PasswordCallback("Password",false); callbackHandler.handle(calls);

The login method tries to connect to the server using the login information that is fetched. If the connection is established, the method returns the value true. The following code snippet shows this:

  boolean verification=false; try{ props.put(Context.SECURITY_PRINCIPAL,cbUserName); props.put(Context.SECURITY_CREDENTIALS,cbPassword); ctx = new InitialDirContext(props); verification=true; } return verification;


This code changes with the actual type of security framework for which the LoginModule is written.


commit() method

This method sets the subject in the session to the username that is validated by the login method. It also populates the subject with roles specified in the LDAP server for that user, and returns true. If the user is not validated, the commit method returns false. The following code snippet shows this:

  if(verification) {subject.getPrincipals().add(userName); ...subject.getPrincipals().add(role); return true; }else return false;


abort() method

This method is used to exit the LoginModule in case of runtime exceptions and is usually triggered by the application server. This method is invoked after the abort() method of LoginContext. The application developer must not directly call the abort method of the LoginContext interface.


logout() method

This method clears the principal settings of the subject in the session. It removes the privilege settings associated with the roles of the subject. The following code snippet shows this:

  subject.getPrincipals().clear(); verification=false; return true;


Exceptions thrown by LoginModule methods

According to the JAAS specifications, all LoginModule methods should only throw a LoginException. Any other exception during LoginModule execution should be caught and a LoginException thrown against it. The following code snippet shows how this can be done:

  public boolean login() throws LoginException { ... catch(IOException e) {throw new LoginException(e.toString());} ...}


Step 2: Writing the CallBackHandler

The CallBackHandler is the JAAS interface that defines the type of data used for authentication. For example, a username-password or a user-certificate combination forms a security identity and credential pair. The type of data used for validating the identity is defined as part of the implementation of the CallBackHandler interface.

The CallbackHandler implementation contains a single method, handle(). The following code snippet from the CallBackHandler distributed with the sample client application ClientLoginSample.java demonstrates this:

  static class MyCallbackHandler implements CallbackHandler { private String username; private String password;


The handle() method sets the value of the username and password attributes, passed by the client application, in the LoginModule's CallBackHandler. The following code snippet shows this:

  handle(){ ... if(callbacks[i] instanceof NameCallback){ NameCallback ncb = (NameCallback)callbacks[i]; ncb.setName(username);} if(callbacks[i] instanceof PasswordCallback){ PasswordCallback pcb = (PasswordCallback)callbacks[i]; pcb.setPassword(password.toCharArray()); }}


Step 3: Configuring the J2EE application

A J2EE application package includes descriptors that contain information about security privileges for various modules/components of the application. A security privilege is defined at the application level and is associated with realms and roles. During deployment, these roles are mapped to roles defined in the server-level realm.

Configuring a login module with a J2EE application involves the following steps:

  1. Creating a login UI that validates user information by calling the LoginContext interface.

  2. Creating application level realms and roles, and mapping permissions to application components.

  3. Distributing the Login Module classes with the application.


Step 4: Packaging the Login Module along with Application

A J2EE application developer would want to configure the LoginModule with a target J2EE application server. Therefore, the LoginModule, along with the helper classes, is packaged into a separate JAR file that may be distributed independently of the application archives, and separately loaded from the server classpath.

For this reason, there must be no code-level dependencies in the LoginModule on the J2EE application.


Step 5: Integrating the Login Module with the J2EE Application Server

The deployment process for a LoginModule is unlike J2EE applications and involves configuring the target application server.

A J2EE application server organizes its security environment into realms, each of which maps to one or more login modules, and has one or more users and roles defined on it. This differs from application realms, which are associated with security permissions for the application and do not assume the existence of any LoginModule.

Integrating a LoginModule with the application server involves the following steps:

  1. Configuring the server with a realm that uses a specific LoginModule for security authentication.

  2. Mapping the application realm and roles to the realm and roles defined by the LoginModule.


Sample LDAP-based LoginModule

The tutorial includes a sample implementation of an LDAP based LoginModule.
Download the required sample files here (jaastutorial.zip).
Get the APIs from Pramati here.

We illustrate the steps to integrate this implementation of the LDAP-based LoginModule with the Pramati Server using a sample application. The sample application uses Pramati's implementation of the User Manager Module, which creates users on the LDAP Server and maps roles to various user names defined on the LDAP server.
Get the User Manager API here.


Installing and configuring the LDAP server

This tutorial is written assuming a particular directory structure on an LDAP Server. If you do not have an LDAP Server installed, you can download one from www.openldap.org. You may access this FTP site.. OpenLDAP Server is distributed for Unix/Linux platforms.

Download the Quickstart Guide fror LDAP and do as follows to create the required directory structure:

  1. Replace the directory names MY-DOMAIN and example with the name my_company.

  2. In the LDAP server configuration file slapd.conf, set password against the name
    rootpw as my_password.

  3. Disable the schema checks on the LDAP server by adding the following line to the slapd.conf file: schemacheck off

  4. After configuring the LDAP Server with the directory structure, start the server as instructed in the Quickstart Guide.




Configuring the LoginModule with Pramati Server

  1. Place the jaastutorial.jar in the classpath of the server by suitably editing setup.bat.

  2. Run setup.bat from the command line.

  3. Start the Server and then start the Console.

  4. Select the Security node in the tree panel on the Console.

  5. In the View Panel, choose an appropriate server realm name, such as LDAP.

  6. In the View Panel, select the Definition tab and then the LoginConfig tab.

  7. Click on the first '+' button. Add the path to the LoginModule class as com.pramati.security.auth.login.LDAPLoginModule.

  8. Specify the LoginModule as Required. 9. Click on the second '+' button to add a name-value attribute for the LoginModule. The module requires multiple name-value pairs and these are listed as LDAP parameters in the section further down.


Configuring the User Manager

  1. Click on the User Manager tab.

  2. Set the fully qualified path of the User Manager class
    tocom.pramati.security.usermanager.LDAPUserManager

  3. Click on the '+' button to add the name-value pairs given in the table one by one. The User Manager needs the same name-value pairs that are in the LoginModule.


After configuring the LoginModule and the UserManager for the LDAP realm, click on Add button to register the LDAP realm with the application server. Ensure that the jaastutorial.jar package is present in the server classpath before starting the server.


Creating users and roles using the User Manager

The User Manager is accessible from the console to enable adding users and roles. If the configuration of the User Manager is correct, new users and their role mappings are added to the realm and can be viewed on the Console.


Setting the client classpath

The ClientLoginSample is a sample Java client that connects to the LDAP server through the Pramati Server. The client classpath is modified to point to sample classes and files, as well as the server classes required to run the Java client. The ClientLoginSample and helper classes are packaged in jaasclient.jar. This must be placed in the client classpath.

The client requires the location of the Pramati Server and the realm to log in to. This information is stored in a file, login.cfg, which is packaged along with this tutorial. This file must be placed in the client classpath.

Edit the login.cfg file to point to the server location and realm name. For example,

com.pramati.realm="realm://127.0.0.1:9191/ldap"

The client also requires the following application server packages in its classpath:

 jaas.jar

jta-spec-01.jar

client.jar

Edit the file jaastutorial.bat to point to the installation directory of the Pramati Server. Run jaastutorial.bat from the command line to set the client classpath.


Running the sample

After setting the client classpath, run the ClientLoginSample program by executing:

java -Djava.security.auth.login.config=<path of login.cfg file>
                 com.pramati.security.Samples.ClientLoginSample <username> <password>

Supply the username and password that have been configured on the LDAP server. The sample program will return the Role to which the User has been mapped. It then logs out the user from the realm. An error message is displayed if the usernameor password submitted is invalid.


Summary

This tutorial demonstrated the simplicity with which JAAS provides standards-based pluggable security. It showed how a custom LoginModule is written and integrated with a typical standards-compliant J2EE application server.