All that JAAS

Scalable Java security with JAAS

Have you ever needed to create a login authentication mechanism for an application? Odds are, you have, and probably more than once, with each new implementation being close, but not identical, to the previous one. For example, one implementation might use an Oracle database, another might use an NT authentication, and another, an LDAP (lightweight access directory protocol) directory. Wouldn't it be nice to support all these security mechanisms without changing any application-level code?

Now in the Java world, you can with the Java Authentication and Authorization Service (JAAS). This relatively new API was an extension in J2SE (Java 2 Platform, Standard Edition) 1.3, is a core API in J2SE 1.4, and is also part of the J2EE (Java 2 Platform, Enterprise Edition) 1.3 specification. In this article, we'll teach you JAAS essentials and show you how to effectively apply JAAS to real-world applications. We based this article's application on our own experiences integrating JAAS into an existing Java Web-based system that used an RDBMS (relational database management system) for storing user login information. With JAAS, we designed more robust, flexible, and consistent login and authentication mechanisms.

You can download a complete set of working examples from Resources below (includes Java sources, JSPs (JavaServer Pages), JAAS configuration, with database and build scripts). We tested these examples using the Resin server with JDBC (Java Database Connectivity) and the MySQL database.

Java Authentication and Authorization: The big picture

Before JAAS, Java's security model was mostly shaped by its origin as a platform-independent language for distributed, networked applications. In its early days, Java often appeared as mobile code, such as browser-based applets, and therefore, the initial security model focused on protecting users based on where the code originated and who created it. Early Java security mechanisms such as SecurityManagers, the sandbox concept, code signing, and policy files were all intended to protect users from the system.

The invention of JAAS reflects Java's evolution into a general-purpose programming language, used for implementing traditional client and server applications that require login and access control. JAAS protects the system from users by allowing or denying access based upon who or what runs the program. While JAAS can perform both authentication and authorization, in this article, we focus primarily on authentication.

JAAS can simplify your Java security development by putting an abstraction layer between your application and disparate underlying authentication and authorization mechanisms. This independence from platforms and algorithms allows you to use different security mechanisms without modifying your application-level code. As with most Java security APIs, JAAS achieves this implementation-independence through an extensible framework of pluggable service provider interfaces (SPIs): a set of abstract classes and interfaces to which specific implementations are developed.

Figure 1 below gives a high-level overview of how JAAS achieves this pluggability. Your application-layer code deals primarily with a LoginContext. Underneath that LoginContext is a set of one or more dynamically configured LoginModules, which handle the actual authentication using the appropriate security infrastructure.

Figure 1. JAAS high-level architecture

JAAS provides some reference LoginModule implementations, such as the JndiLoginModule; you can also develop your own, as we'll do here with the RdbmsLoginModule. We'll also show how you can quickly set up an application with a choice of implementations using a simple configuration file.

In addition to being pluggable, JAAS is stackable: in the context of a single login, a set of security modules can stack on top of one another, each called in order and each interacting with a different security infrastructure.

JAAS aspects are modeled on some familiar security architectural patterns and existing frameworks. The stackable feature, for example, deliberately resembles the Unix Pluggable Authentication Module (PAM) framework. From a transactional viewpoint, JAAS adopts behaviors similar to two-phase commit (2PC) protocols. JAAS's security configuration concepts, including Policy files and Permissions, come from the J2SE 1.2 security packages. JAAS also borrows ideas from other established security frameworks, such as X.509 certificates, from which the name Subject is derived (you'll learn more about Subject later).

Note: JAAS is just one of several new Java security APIs. For more on Java security, see the sidebar "The Java Security Puzzle" and Resources below.

Client- and server-side JAAS

You can apply JAAS on both the client and the server. Using it on the client side is straightforward, as we'll demonstrate shortly. On the server-side things grow a bit more complex. Currently, JAAS in the application server market is a bit inconsistent; J2EE app servers use JAAS slightly differently, depending on which one you use. For example, JBossSX, using their own architecture, nicely integrates JAAS into its overall security framework (which is detailed in Scott Stark's excellent JavaWorld article "Integrate Security Infrastructures with JBossSX" (August 2001)). However, though WebLogic 6.x supports JAAS, the details differ.

So you can understand JAAS from both server- and client-side perspectives, we'll demonstrate examples of both in this article. And for the purposes of simplicity on the server, we'll use the Resin application server so we can start off with a cleaner slate (Resin does have a pluggable authentication scheme of its own, but it's nonstandard, so using JAAS gives us more portability options later).

Core JAAS

To get started with JAAS, you must first ensure it's installed. J2SE 1.4 already includes JAAS; J2SE 1.3 does not. If you want to continue to use J2SE 1.3, download JAAS from Sun Microsystems. Once you download and install JAAS to a given directory, you will see a subdirectory called lib, which contains one file named jaas.jar. You'll need to add this file to your classpath or copy it to your JRE (Java Runtime Environment) extensions directory (in <jre-home>\lib\ext, where <jre-home> is your JRE's location). You are then JAAS-ready. Note: If you use an application server, it may already include JAAS. Check your server's documentation for details.

With any of these approaches, note that you can change some of the JAAS-related system property settings (as well as many other Java security settings) in the Java security properties file. This file, java.security, is located in the <jre-home>/lib/security directory and written in the standard Java properties file format.

Using JAAS authentication from your application typically involves the following steps:

  1. Create a LoginContext
  2. Optionally pass a CallbackHandler to the LoginContext, for gathering or processing authentication data
  3. Perform authentication by calling the LoginContext's login() method
  4. Perform privileged actions using the returned Subject (assuming login succeeds)

Here's a minimal example:

    LoginContext lc = new LoginContext("MyExample");
    try {
        lc.login();
    } catch (LoginException) {
        // Authentication failed.
    }
    // Authentication successful, we can now continue.
    // We can use the returned Subject if we like.
    Subject sub = lc.getSubject();
    Subject.doAs(sub, new MyPrivilegedAction());

Underneath the covers, a few other things occur:

  1. During initialization, the LoginContext finds the configuration entry "MyExample" in a JAAS configuration file (which you configured) to determine which LoginModules to load (see Figure 2)
  2. During login, the LoginContext calls each LoginModule's login() method
  3. Each login() method performs the authentication or enlists a CallbackHandler
  4. The CallbackHandler uses one or more Callbacks to interact with the user and gather input
  5. A new Subject instance is populated with authentication details such as Principals and credentials

We'll explain further details below, but to begin, let's look at the key JAAS classes and interfaces involved in the process. These are typically divided into the following three groups:

Table 1. JAAS classes and interfaces
CommonSubject, Principal, credential (credential is not any specific class, but can be any object)
AuthenticationLoginContext, LoginModule, CallbackHandler, Callback
AuthorizationPolicy, AuthPermission, PrivateCredentialPermission

Most of these classes and interfaces are in the javax.security.auth package's subpackages, with some prebuilt implementations in the com.sun.security.auth package, included only in J2SE 1.4.

Note: Because we focus on authentication in this article, we don't delve into the authorization classes.

Common: Subjects, Principals, and Credentials

The Subject class represents an authenticated entity: an end-user or administrator, or a Web service, device, or another process. The class contains three sets of security information types:

  • Identities: In the form of one or more Principals
  • Public credentials: Such as name or public keys
  • Private credentials: Like passwords or private keys

Principals represent Subject identities. They implement the java.security.Principal interface (which predates JAAS) and java.io.Serializable. A Subject's most important method is getName(), which returns an identity's string name. Since a Subject instance contains an array of Principals, it can thus have multiple names. Because a social security number, login ID, email address, and so on, can all represent one user, multiple identities prove common in the real world.

The last element here, credential, is not a class or an interface, but can be any object. Credentials can include any authentication artifact, such as a ticket, key, or password, that specific security systems might require. The Subject class maintains unique Sets of private and public credentials, which can be retrieved with methods such as getPrivateCredentials() and getPublicCrendentials(). These methods are more often used by security subsystems than at the application layer.

Authentication: LoginContext

Your application layer uses LoginContext as its primary class for authenticating Subjects. LoginContext also represents where JAAS's dynamic pluggability comes into play, because when you construct a LoginContext, you specify a named configuration to load. The LoginContext typically loads the configuration information from a text file, which in turn tells the LoginContext which LoginModules to use during login.

The three commonly used methods in LoginContext are:

Table 2. LoginContext methods
login()Performs login, a relatively complex step that invokes all LoginModules specified for this configuration. If it succeeds, it creates an authenticated Subject. If it fails, it throws a LoginException.
getSubject() Returns the authenticated Subject.
logout()Logs out the authenticated Subject and removes its Principals and credentials.

We will show how to use these methods later.

Authentication: LoginModule

LoginModule is the interface to specific authentication mechanisms. J2SE 1.4 ships with a set of ready-to-use LoginModules, including:

Table 3. LoginModules in J2SE 1.4
JndiLoginModuleVerifies against a directory service configured under JNDI (Java Naming and Directory Interface)
Krb5LoginModule Authenticates using Kerberos protocols
NTLoginModuleUses the current user's NT security information to authenticate
UnixLoginModuleUses the current user's Unix security information to authenticate

Along with these modules comes a set of corresponding concrete Principal implementations in the com.sun.security.auth package, such as NTDomainPrincipal and UnixPrincipal.

The LoginModule interface has five methods:

Table 4. LoginModule methods
initialize()Called after the LoginModule is constructed.
login() Performs the authentication.
commit()Called by the LoginContext after it has accepted the results from all LoginModules defined for this application. We assign Principals and credentials to the Subject here.
abort()Called when any LoginModule for this application fails (even though earlier ones in sequence may have succeeded—thus akin to a 2PC model). No Principals or credentials are assigned to the Subject.
logout()Removes the Principals and credentials associated with the Subject.

The application layer calls none of these methods directly—the LoginContext invokes them as needed. Our example below will elaborate on these methods' implementations.

Authentication: CallbackHandlers and Callbacks

CallbackHandlers and Callbacks let a LoginModule gather necessary authentication information from a user or system, while remaining independent of the actual interaction mechanism. We'll leverage that capability in our design—our RdbmsLoginModule does not depend on how the user credentials (username/password) are obtained and can thus be used in the different application environments we will illustrate (either from the command line or from a JSP).

1 2 3 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more