Developing an Enterprise Security Model Using JAAS and JBoss

Developing an Enterprise Security Model Using JAAS and JBoss

By Brian M. Coyner, OCI Software Engineer 

July 2002


Introduction

Securing access to enterprise resources is critical to any Java 2 Platform Enterprise Edition (J2EE) application. Anyone building a project that has security requirements (and what project doesn't?) should immediately begin to design the security infrastructure and prepare to refactor as the project matures.

This article will help shine some light on some of the most important aspects of developing and implementing an enterprise security framework.

The Product

This article's application will make use of a fictional online e-commerce store. This store will have a catalog full of fabulous merchandise that customers will be able to view and purchase.

Along with the online storefront is an entire back office of managers and administration staff. These people are responsible for ensuring that the site is successful.

Of course we are not going to actually develop a full-blown online store, but rather just use some of the concepts to help motivate the article.

Please refer to Resources for information on how to download and run the sample application.

Here are a few high level requirements:

These requirements are simple but will help motivate the concepts needed to successfully master the art of enterprise security.

Section 1: The Security Model

One of the first steps taken when designing a security framework is to think of the business functionality or business functions that your system will need in order to work. A business function may be a task to add new items to an online catalog or update customer account information. A business function may also be a logical grouping of tasks executed within a single atomic transaction.

Defining Users

We know that enterprise systems need the concept of a user. When you log into your personal computer (PC) at work, you are logging in as a user. When you access your email account, you do so as a user. So, a user is defined as a single entity that is trying to access a resource. We will see in the next section how a user fits into the JAAS framework.

Users typically have certain privileges. At work you probably do not have access to accounting information, but the accounting staff and president will. And, the accounting staff probably does not have access to the source-code repository where your projects are stored. We can think of this as users that have roles.

A role can be described as a way to logically control access to a single business function.

So, what does that mean?

Let's think back to the requirements. We know that we need to allow managers the ability to view information on consumers' buying habits. To satisfy this requirement, we assign this user the role of 'ViewUserStatistics'. The role name is arbitrary; we could have called it 'TheSunIsBright', but that would not be very descriptive.

The same steps should be repeated for all business functions in your application.

But, how far should you go? Should each method in your application define its own role? Should each class define its own role?

The goal should be to provide enough granularity without sacrificing functionality and ease of use. A role should be at a slightly higher level of abstraction than a single method. You may find at times it makes sense to simply have one role per method. This is okay at first, but as the system grows, you will need to take a step back and refactor to ensure that the correct level of granularity is being achieved.

Table 1-1 cross-references our requirements with our users and their roles.

Table 1-1 (a): Requirement to Role Mappings

Table 1-1 A

Table 1-1 (b): User to Role Mappings

Table 1-1 B

The data shown above barely scratches the surface on the users and roles that may be necessary in an enterprise application. Even the smallest enterprise application may contain dozens of roles. Large enterprise applications may contain hundreds. The challenge that you will most likely face is anticipating the roles that will be needed for your application and managing the changes that occur.

If your application grows to have hundreds of roles, you may wish to consider adding another level of abstraction, called groups, to the security model.

By grouping roles together, we can assign users to one or more groups, where each group contains numerous roles.

The best way to help motivate this is to use another example.

Let's assume that our company just hired another administrator named Suzy. Suzy will need to have all the roles of an administrator. Which, if we remember, are 'ManageUsers' and 'UpdateCustomer'.

This may not seem like a big deal, since we only have two roles that our administrators use. But, what if there were hundreds of roles? We would have to assign each role to Suzy, possibly missing one or two in the process. By adding in the concept of a group, we only have to assign Suzy to be in the 'Administrator' group.

Tables 1-2(a) and 1-2(b) show how the new groups affect our original design. And just for fun, I tossed in another manager named John.

Table 1-2 (a): Group to Role Mappings

Table 1-2 A

Table 1-2 (b): User to Group Mappings

Table 1-2 B

The concept of grouping roles together is only used for managing users and their roles; it does not have any bearing on deploying J2EE applications. We will see in section three how this information is pulled together to provide secure access to J2EE resources.

We now have the high-level concepts of what users, groups, and roles our application will be using. Next, we will turn our attention to some of the guts of the application by looking at our data store design.

Creating the Foundation

The next step is to create the foundation for our application.

Our application will consist of a persistent data store to hold our user, group, and role data, a couple of Stateless Session Beans (deployed to JBoss), and numerous unit tests.

We will make use of Ant to compile, deploy and run our application.

Designing the Data Store

Persistent data is critical for any enterprise application. Our small application will use MySQL as the persistent data store. MySQL is an open-source relational database. See Resources for download information. This article does not focus on the details of MySQL. Ant targets have been written to create and populate the database.

Another alternative to using a database for user, group, and role information is to use a directory service, such as Lightweight Directory Access Protocol (LDAP). Directory services are accessed using Java Naming and Directory Interface (JNDI). This is beyond the scope of the article, but the concepts are the same. The only difference is the underlying technology.

User information needs to be retrieved from a data store if we are to authenticate users trying to access our application.

Users will need a unique id, username, and password. The username could be used as the unique id, but it is more efficient and flexible to use an integer-based key.

One reason to choose this approach is to allow users to change their username without changing the primary key. Keep in mind that application logic will need to be implemented to ensure that usernames are not duplicated. This may be implemented simply by adding a unique constraint to that column in the database or by programmatically ensuring that no duplicates are entered.

Next, we need to determine how to manage the authentication and authorization data for our users.

We have already determined that we will have multiple users that may belong to multiple groups, and each group may have multiple roles. Diagram 1-1 shows the data store for our application. As you can see there is not much to it, but it does provide a flexible and powerful tool for managing users and roles.

Diagram 1-1: Data Store Design

Diagram 1.1

Creating the Enterprise Java Beans (EJB)

Creating the EJBs is the next step. Surprisingly, these beans will not access the data store just created. So, how exactly are the users going to be authenticated? We are going to make use of some very powerful Application Programming Interfaces (API) to lend us a helping hand. More specifically, we will use the JAAS framework. This framework is discussed in the next section.

In reality, an application would use Entity Beans to create, modify, and remove users, groups, and roles. In order to keep things focused, scripts create our database automatically.

There are two Stateless Session Beans:

The listings below show remote interfaces for the two beans.

  1. package com.ociweb.ejb;
  2.  
  3. import javax.ejb.EJBObject;
  4. import java.rmi.RemoteException;
  5.  
  6. public interface CatalogMgr extends EJBObject {
  7.  
  8. int getCurrentQuantity(String itemID) throws RemoteException;
  9. Object findItem(String itemID) throws RemoteException;
  10. void purchaseItem(String itemID) throws RemoteException;
  11. }
  12.  
  13.  
  14. package com.ociweb.ejb;
  15.  
  16. import javax.ejb.EJBObject;
  17. import javax.ejb.FinderException;
  18. import java.rmi.RemoteException;
  19. import java.util.Collection;
  20.  
  21. public interface UserMgr extends EJBObject {
  22.  
  23. Collection getDemographicInfo() throws RemoteException;
  24. Collection getPurchasingHistory(int userID) throws RemoteException;
  25. void updateUser(int userID, String newUsername, String newPassword)
  26. throws RemoteException;
  27. Object findUser(int userID) throws RemoteException, FinderException;
  28. }

If you look at the implementation classes for each of the beans (UserMgrBean and CatalogMgrBean), you will notice that we do not code any security logic. This is because zero lines of code need to be written to determine if the calling client has access to the bean's methods.

This is the beauty of using J2EE technology!

We are able to keep our business logic separated from security management, transaction management, and all other low-level resource activities that would simply get in the way of coding the bean. Instead EJBs are configured using a deployment descriptor.

An EJB deployment descriptor is an eXtensible Markup Language (XML) file. The structure of this file is governed by a Document Type Definition (DTD) produced by Sun Microsystems.

A deployment descriptor allows J2EE applications to be configured without the need for hard coding configuration options. We will soon see how to use a deployment descriptor to provide declarative security. Refer to Resources for more information on EJB and XML.

The EJB deployment descriptor is called ejb-jar.xml and contains the information needed to configure EJBs for deployment, including security attributes.

There are two main sections to an EJB deployment descriptor:

  1. The first section defines each bean.
  2. The second section defines how the bean's methods behave in transactional and security realms.

Describing a bean means that we specify the name of the bean, the home and remote interfaces, the implementation class, and the transactional and security information.

The listing below shows how we define the CatalogMgrBean (ejb-jar.xml).

  1. <ejb-jar>
  2. <enterprise-beans>
  3. <session>
  4. <ejb-name>CatalogMgrBean</ejb-name>
  5. <home>com.ociweb.ejb.CatalogMgrHome</home>
  6. <remote>com.ociweb.ejb.CatalogMgr</remote>
  7. <ejb-class>com.ociweb.ejb.CatalogMgrBean</ejb-class>
  8. <session-type>Stateless</session-type>
  9. <transaction-type>Container</transaction-type>
  10. <security-role-ref>
  11. <description>
  12. Users of this Role can view Inventory Levels
  13. </description>
  14. <role-name>ViewInventoryLevels</role-name>
  15. <role-link>ViewInventoryLevels</role-link>
  16. </security-role-ref>
  17. <security-role-ref>
  18. <description>
  19. Users of this Role can search
  20. </description>
  21. <role-name>BookSearch</role-name>
  22. <role-link>SearchCatalog</role-link>
  23. </security-role-ref>
  24. <security-role-ref>
  25. <role-name>PurchaseItem</role-name>
  26. <role-link>PurchaseItem</role-link>
  27. </security-role-ref>
  28. </session>
  29. </enterprise-beans>
  30. . . .
  31. </ejb-jar>

The <security-role-ref> element is used to define the security roles used by a bean implementation and to link that security role to a role defined in the assembly descriptor section of the deployment descriptor.

In the above listing, the CatalogMgrBean refers to the following roles:

This means that we may write the following code in our bean implementation:

  1. EJBContext ejbContext = . . .
  2. if (ejbContext.isCallerInRole("BookSearch")) {
  3. //do something
  4. }
  5.  
  6. if (ejbContext.isCallerInRole("ViewInventoryLevels")) }
  7. //do something else
  8. }

But wait a minute . . .

The 'BookSearch' role is not defined in our data model (see Table 1-1). How does this work?

Sometimes bean developers may not know the security domain, so they may simply make up a name that best describes what they are trying to secure.

In the above example, the bean developer may have been trying to ensure that the caller was able to search for books. Before the application is deployed the 'BookSearch' role, used by the bean developer, has to be mapped to a role name described in the assembly descriptor section of the deployment descriptor. The <role-link> element is used to accomplish the task of linking or mapping role names. Finally, the <description> element should be used to provide comments about the role names and their intended purpose.

After the beans have been defined, they can be assembled. Assembling a bean means to define the behavior of each of the bean's methods in the operating environment.

We describe this behavior under the <assembly-descriptor> tag as shown in the listing below.

  1. <ejb-jar>
  2. . . .
  3. <assembly-descriptor>
  4. <security-role>
  5. <role-name>ViewInventoryLevels</role-name>
  6. </security-role>
  7. <security-role>
  8. <role-name>SearchCatalog</role-name>
  9. </security-role>
  10. <security-role>
  11. <role-name>PurchaseItem</role-name>
  12. </security-role>
  13. <method-permission>
  14. <unchecked></unchecked>
  15. <method>
  16. <ejb-name>CatalogMgrBean</ejb-name>
  17. <method-name>create</method-name>
  18. </method>
  19. </method-permission>
  20. <method-permission>
  21. <role-name>ViewInventoryLevels</role-name>
  22. <method>
  23. <ejb-name>CatalogMgrBean</ejb-name>
  24. <method-name>getCurrentQuantity</method-name>
  25. </method>
  26. </method-permission>
  27. <method-permission>
  28. <role-name>SearchCatalog</role-name>
  29. <method>
  30. <ejb-name>CatalogMgrBean</ejb-name>
  31. <method-name>findItem</method-name>
  32. </method>
  33. </method-permission>
  34. <method-permission>
  35. <role-name>PurchaseItem</role-name>
  36. <method>
  37. <ejb-name>CatalogMgrBean</ejb-name>
  38. <method-name>purchaseItem</method-name>
  39. </method>
  40. </method-permission>
  41. </assembly-descriptor>
  42. </ejb-jar>

The <security-role> element defines a logical role name. This role name is not directly tied to a security domain.

This means that the 'ViewInventoryLevels' does not have a particular level of security associated with it. It is up to the application deployer to map the 'ViewInventoryLevels' role to that of a company manager for our application. Luckily, we have already done this for our application because we are acting as the implementer, assembler, and deployer.

The <method-permission> element is used to define what security roles each bean method will secure.

For example, if we tried to access the purchaseItem() method on CatalogMgrBean, the J2EE server would first check to ensure that we had the 'PurchaseItem' role name assigned to us.

If we want to tell the J2EE server to allow any caller access to a bean's method, we can use the <unchecked/> element in place of the <role-name> element.

The above listing shows how this is written for the create() method of CatalogMgrBean.

Diagram 1-2 provides a look at each piece of the puzzle and how those pieces relate.

Diagram 1-2: Deployment Descriptor Role Linking

Diagram 1.2

Security Model Summary

The concepts described in this section can be used in any J2EE application environment and are not specific to any vendor.

An important point to remember is that a business function may span more than one bean. Therefore, care must be taken to ensure that the correct roles are assigned to allow the caller to successfully complete a business function that may span several beans and several methods.

The Session Facade pattern is a great example to demonstrate this point. One of the most challenging aspects to designing a robust and flexible security model is understanding how all of the pieces need to interact, especially in a J2EE environment where a lot of the concepts and strategies are very abstract.

Section 2: JAAS

The concept of Java security is nothing new. Java security technology has been around since the dawn of Java and is used to provide a safe environment to run potentially untrusted code.

As of the release of the Java 2 platform, we are now capable of placing fine-grained controls on our resources. Each resource can be secured with regard to the identity of where the code came from and who digitally signed that code.

For example, you may download a digitally signed application via Java WebStart and can be sure that the code is trustworthy and secure (as long as you trust the signer).

This type of security has its limitations. One major limitation is that there is no control over who has access to the application. Or even more importantly how does the application limit access to different business functions?

What is JAAS?

The JAAS framework is complimentary to the Java 2 platform security model and has been integrated into Java 1.4.

JAAS provides the following:

Authentication
Reliably and securely determine who is executing Java code.

The term authentication represents the process by which one Subject (user) verifies he is who he says he is. The only catch is that the authentication process should – or more appropriately, must – be accomplished in a secure fashion, usually with Secure Sockets Layer (SSL).
Authorization
Ensure users have the correct permissions to access a resource. Do you have access to view customer-buying habits?

JAAS authentication (logging into an application) is performed in a pluggable manner. This simply means that the application is not directly tied to a particular authentication technology. As stronger authentication technologies emerge, your application can simply 'plug' it in.

Authentication technologies vary in strength and complexity. Authentication technologies might include encrypting a username and password (probably accepted from a user interface) that is sent across the wire to a remote server where the information is decrypted and verified. More complex authentication technologies might include biometric devices (retinal scanners or thumbprint readers).

So what does this mean? How do I use JAAS?

To answer these questions and many others we must first take a look at some of the core classes and interfaces provided in the JAAS framework.

JAAS Core Classes and Interfaces

Subject

Subject refers to any entity requesting access to a service. A subject may represent a person or another service.

Typically we think of people and services as having names. For instance, a person may have the name 'Bob' and a server may have the name 'Integra'. We can use these names to establish connections with a resource (via a look up table, naming service, etc).

Of course we know that people often have numerous identities. In some situations using the name 'Bob' might successfully identify that person. In other situations it may be necessary to identify that person using a unique id.

Another way to think of it is like this:

You are the Subject. You have a name; you have an employee id (hopefully!); you have a bank account number. Your name might uniquely identify you within your group of friends. Your employee id tells your boss whom to send the check to on payday. Your bank account number identifies the account that holds the money your boss just sent. Each identity is a Principal and is used to help identify you in distinct scenarios.

Subject in the JAAS realm is represented by a javax.security.auth.Subject.

Subject represents a collection of related information for a single entity. The information stored may include the identities of the Subject, which as we saw above may be a name or id and security-related information (credentials) such as passwords, keys (public and private), etc.

Subject identities (name, id, etc) are stored as java.security.Principal objects, and credentials are stored as java.lang.Objects. Thus any object may be a credential.

A Subject may have one or more Principals and one or more credentials. Listed below are some of the key methods in the Subject class.

public Set getPrincipals();
public Set getPublicCredentials();
public Set getPrivateCredentials();

Client applications will almost never instantiate a Subject directly, but rather the Subject will be instantiated by a javax.security.auth.login.LoginContext.

We will look at LoginContext in just a bit.

Principals

As mentioned above, a Principal object represents a single identity for a Subject. A Principal is an interface that defines four methods that concrete implementations are required to implement.

String getName();
boolean equals(Object another);
public String toString();
public int hashCode();

Principal objects become associated with a Subject upon successful authentication. The Principal will remain with the Subject until the Subject is logged out.

Credentials

Credentials describe security-related information such as passwords, encrypted keys, or public key certificates (X.509, PGP, etc). JAAS credentials are represented by any object (java.lang.Object) for ultimate flexibility.

JAAS credentials are divided into two categories:

  1. The first category contains public credentials (public key certificates, etc.).
  2. The second category contains private credentials (passwords, encrypted keys, etc.).

Credential information is associated with a Subject upon successful authentication.

LoginContext

A LoginContext is used to authenticate a Subject and is represented by the class javax.security.auth.login.LoginContext.

A LoginContext is configured with one or more authentication technologies or LoginModules. This technique is known as LoginModule stacking, which simply means that all configured LoginModules must succeed or fail.

Configuring the application with a set of LoginModules allows for an application to change authentication technologies without recompiling.

LoginContext uses a configuration to retrieve the configured LoginModules. The configuration is discussed later in this section.

The LoginContext defines the following methods.

void login() throws LoginException;
void logout() throws LoginException;
void Subject getSubject();

LoginContext performs authentication in two phases:

  1. The first phase is known as the login phase. During this phase each configured LoginModule is told to 'login' (perform authentication). If all configured LoginModules pass phase one, the LoginContext enters phase two.
  2. In phase two, each configured Login Module 'commits' the relevant Principals and credentials to the Subject.

If at any time the first or second phase fails, each configured LoginModule is told to 'abort' the login process (clean up any saved state). If the authentication succeeds, the application may ask the LoginContext for the authenticated Subject.

The entire login process is completely transparent to an application writer because the LoginContext manages the authentication process. An application knows if the authentication succeeded when no exceptions are thrown. If the authentication fails then a javax.security.auth.login.LoginException is thrown.

LoginModules

As mentioned above, a LoginModule represents a single authentication technology. A LoginModule is represented by the interface javax.security.auth.spi.LoginModule and provides the following methods.

boolean login();  // phase one
boolean commit(); // phase two
boolean abort();
boolean logout();

Application writers typically do not implement LoginModules, but rather configure the LoginContext with a set of LoginModule implementations. Refer to Resources for information on how to write a LoginModule.

Later in this section we will look at a simple LoginModule implementation called SimpleLoginModule.

Callbacks and CallbackHandlers

LoginModule sometimes needs to communicate with the user to obtain authentication information. This is typically done via a user interface screen, command-line prompt, or even a biometric device (thumb print reader or retinal scanner). 

CallbackHandlers give a LoginModule a way to communicate with the user that is independent of the underlying authentication technology. CallbackHandlers are represented by the interface javax.security.auth.callback.CallbackHandler.

CallbackHandler defines only one method.

void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException;

An implementation of a CallbackHandler may prompt the user with a slick graphical user interface to retrieve a username and password, or it may simply use a command line interface.

The LoginModule will pass the CallbackHandler an array of javax.security.auth.callback.Callbacks.

Callback is a marker interface that simply tells the CallbackHandler that it is used to hold authentication information. Listed below are two Callback implementations that you will commonly use.

javax.security.auth.callback.NameCallback
javax.security.auth.callback.PasswordCallback

As you can tell by the names, the NameCallback is used to hold a username and the PasswordCallback is used to hold a password.

Callback implementations should provide a way to communicate information from the application to the underlying security service and are normally written by the LoginModule writer.

LoginModule Configurations

LoginModule configuration specifies which LoginModules should be used for authentication in an application.

Equally important is that the order the LoginModules are configured is the order in which they are invoked. Thus if one LoginModule depends on another, you must ensure they are configured in the correct order.

Recall that the LoginContext will consult this configuration to determine which LoginModules to load. Listed below is the configuration template. This information is typically stored in a file called auth.conf.

Configuration Template

  1. Application_1 {
  2. ModuleClass_InvokedFirst Flag
  3. ModuleOption1=Value1
  4. ModuleOption2=Value2
  5. ModuleOptionN=ValueN
  6. ;
  7.  
  8. ModuleClass_InvokedSecond Flag
  9. ModuleOption1=Value1
  10. ModuleOption2=Value2
  11. ModuleOptionN=ValueN
  12. ;
  13.  
  14. // this is a comment
  15. // there can be many Modules defined here
  16.  
  17. ModuleClass_InvokedLast Flag
  18. //options are optional
  19. ;
  20. };
  21.  
  22. Application_2 {
  23. ModuleClass_InvokedFirst Flag
  24. ModuleOption1=Value1
  25. ModuleOption2=Value2
  26. ModuleOptionN=ValueN
  27. ;
  28.  
  29. ModuleClass_InvokedSecond Flag
  30. // options are optional
  31. ;
  32.  
  33. ModuleClass_InvokedLast Flag
  34. ModuleOption1=Value1
  35. ModuleOption2=Value2
  36. ModuleOptionN=ValueN
  37. ;
  38. };
  39.  
  40. // this is the default application name
  41. other {
  42. ModuleClass_InvokedFirst Flag
  43. ModuleOption1=Value1
  44. ModuleOption2=Value2
  45. ModuleOptionN=ValueN
  46. ;
  47.  
  48. ModuleClass_InvokedSecond Flag
  49. // options are optional
  50. ;
  51.  
  52. ModuleClass_InvokedLast Flag
  53. ModuleOptions1=Value1
  54. ModuleOption2=Value2
  55. ModuleOptionN=ValueN
  56. ;
  57. };

Each entry in the configuration is referenced with an application name. The application name is used to look up or index the set of LoginModules that an application using JAAS will use for authentication.

Under each application is a list of one or more fully qualified LoginModule name(s). Each LoginModule is invoked in the order in which it appears. This technique is called LoginModule stacking and provides a powerful means to stack or chain together different LoginModules to authenticate a single Subject.

The Flag value controls the behavior of the authentication stack. The following values are valid for the Flag attribute:

ModuleOptions are space-delimited properties (name/value pairs) that are unique to a given LoginModule. Each LoginModule controls the definition of its properties.

Listed below is a code example to help illustrate.

  1. package com.ociweb.auth.login;
  2.  
  3. public class DatabaseLoginModule implements LoginModule {
  4.  
  5. private String dbDriver;
  6. private String dbURL;
  7. private String pwQuery;
  8.  
  9. public void initialize(Subject subject, CallbackHandler callbackHandler,
  10. Map sharedState, Map options) {
  11. . . .
  12. this.dbDriver = (String) this.options.get("dbDriver");
  13. this.dbURL = (String) this.options.get("dbURL");
  14. this.pwQuery = (String) this.options.get("pwQuery");
  15. . . .
  16. }
  17. }

Here is what the configuration file auth.conf may look like:

  1. DBLogin {
  2. com.ociweb.auth.login.DatabaseLoginModule required
  3. dbDriver="org.gjt.mm.mysql.Driver"
  4. dbURL="jdbc:mysql://localhost/security_examples"
  5. pwQuery="SELECT password FROM Users WHERE username=?"
  6. ;
  7. };

The Authentication Process (Graphical User Interface Login)

Next, we will walk through an example of a JAAS login using a Swing Graphical User Interface.

The example we are about to walk through is meant to give an overview of the authentication process and not on strong authentication technologies. Thus proper error handling and topics like encryption are omitted to keep the example focused. This example can be downloaded from Resources.

The first step in our authentication process is to show a LoginDialog.

// show the dialog
new LoginDialog().setVisible(true);

Diagram 2-1: Login Screen

Diagram 2.1 Login Screen

Now the user will enter his username and password and click the 'Login' button.

Listed below is the code that executes when 'Login' is pressed.

  1. public void actionPerformed(ActionEvent e) {
  2. String appName = System.getProperty("com.ociweb.jaas.appname");
  3. SimpleCallbackHandler handler =
  4. new SimpleCallbackHandler(username.getText(), password.getPassword());
  5. try {
  6. LoginContext context = new LoginContext(appName, handler);
  7. // if an exception is not thrown then the
  8. // login succeeded.
  9. context.login();
  10. JOptionPane.showMessageDialog(this.owner, "Success", "Login Successful",
  11. JOptionPane.INFORMATION_MESSAGE);
  12. } catch (LoginException ex) {
  13. showErrorMessage("Login Failed", ex.getMessage());
  14. username.requestFocus();
  15. } catch (Exception ex) {
  16. showErrorMessage("Login Failed", ex.getMessage());
  17. username.requestFocus();
  18. }
  19. }

First, the JAAS configuration application name is retrieved from the property com.ociweb.jaas.appname. Recall that this is the name in the auth.conf file that tells the LoginContext which LoginModules to invoke.

The next step is to retrieve the username and password entered by the user. This information is given directly to the CallbackHandler.

This is one approach to solving the problem. Another approach would be to have the SimpleCallbackHandler itself launch the GUI and set it's own data.

LoginContext is instantiated with the JAAS configuration application name and the SimpleCallbackHandler.

Next, the LoginContext.login() method is invoked to begin the authentication process. If the authentication process succeeds then the user sees a nice message. Otherwise, a failure message is shown.

The LoginContext uses the JAAS application name to locate the set of LoginModules that will be used to authenticate the new Subject. In this example the auth.conf file looks similar to this:

  1. SimpleLogin {
  2. com.ociweb.auth.login.SimpleLoginModule required
  3. ;
  4. };

The only LoginModule that will be invoked is the SimpleLoginModule. This LoginModule, as we will see in a bit, simply checks that the username is 'superman' and the password is 'secret'. (Obviously this is not a very strong authentication technology, but does provide a nice introduction into the JAAS world.)

We know that the LoginContext performs authentication in two phases. The first phase performs authentication and the second phase commits the authentication information to a Subject.

Listed below is how the SimpleLoginModule performs its authentication.

Phase 1

  1. public boolean login() throws LoginException {
  2. Callback[] callbacks = new Callback[2];
  3. callbacks[0] = new NameCallback("Username:");
  4. callbacks[1] = new PasswordCallback("Password:", false);
  5.  
  6. try {
  7. this.callbackHandler.handle(callbacks);
  8. } catch (IOException ex) {
  9. throw new LoginException("Failure to retrieve user input: "
  10. + ex.getMessage());
  11. } catch (UnsupportedCallbackException ex) {
  12. throw new LoginException("Programmer Error! Callback not supported: "
  13. + ex.getMessage());
  14. }
  15.  
  16. this.username = ((NameCallback) callbacks[0]).getName();
  17. this.password = ((PasswordCallback) callbacks[1]).getPassword();
  18.  
  19. if (!"superman".equals(this.username)) {
  20. this.succeeded = false;
  21. throw new FailedLoginException("Invalid User Name: " + this.username);
  22. }
  23.  
  24. if (!"secret".equals(new String(this.password))) {
  25. this.succeeded = false;
  26. throw new FailedLoginException("Invalid Password");
  27. }
  28.  
  29. this.succeeded = true;
  30. return this.succeeded;
  31. }

There is not much to the authentication process in this example. We simply retrieve the information from the Callbacks and compare the username to 'superman' and the password to 'secret'.

The second phase commits the authentication information to a Subject. The listing below shows how the SimpleLoginModule handles the commit process.

Phase 2

  1. public boolean commit() throws LoginException {
  2. if (this.succeeded) {
  3. this.principal = new OCIPrincipal(this.username);
  4. this.subject.getPrincipals().add(this.principal);
  5.  
  6. cleanUp();
  7. return true;
  8. } else {
  9. cleanUp();
  10. return false;
  11. }
  12. }

During the commit phase we check to see if the login succeeded. If the login phase succeeded, then we instantiate a new Principal and add it to the Subject.

Notice here that we did not store the password in the private credentials, although we probably should.

The last part of the commit phase is to clean up any internal state. The instance variables principal and password are no longer needed and should be wiped from memory.

JAAS Summary

By now you should have a good understanding of the JAAS framework to tackle security-related challenges. Just remember that most application developers will probably never write their own LoginModules. This is best left to the security experts. Application writers will write the necessary CallbackHandlers and create a LoginContext configuration (auth.conf).

In the next section, we will look at how to configure JBoss to use an implementation of the Secure Remote Password (SRP) authentication technology.

SRP is a strong public-key handshake protocol. Refer to the Resources for links to SRP technology.

Section 3: Configuring JBoss Security

So far, we have discussed how a security model may be implemented and the power of the JAAS framework. It is time now to put our ideas to work by configuring a J2EE application server, which in our case is the open-source JBoss server, to secure access to our EJBs.

This section walks through how to configure JBoss to secure access to EJBs.

MBeans

JBoss uses the Java Management Extension (JMX) as its architectural backbone. JMX defines the concept of a Managed Bean (MBean).

An MBean is a Java class that implements an MBean interface (there are four types) and follows a set of well-defined patterns.

MBeans are configurable, thus allowing changes to the behavior of the server without having to recompile. This means that functionality or services may be added and removed simply by changing a configuration file.

JBoss 2.4.5 uses an XML file called jboss.jcml to configure all MBeans used by an instance of the server. This file is proprietary to JBoss. The important point to take from this is that we will be configuring numerous MBeans to tweak how the JBoss server runs. Please refer to References for links to JMX resources.

At the end of this section is a custom MBean service used to assist with database access using the SRP protocol. This MBean is not part of the JBoss distribution.

Installing JBoss

Now is a good time to install the necessary tools needed for our application. Please follow the instructions located in the resources bundle to accomplish this task.

. . . about 20 minutes later . . .

At this point you should have the following open-source tools installed.

  1. JBoss (2.4.5) – free, J2EE-compatible server
  2. JUnit (3.7) – unit testing framework
  3. MySQL (3.23.38) – open-source relational database
  4. Ant (1.4.1) – open-source build tool
  5. JDK (1.4)

All of the code for this article was written and tested with the versions of the tools listed above.

JBoss Directory Structure

Let's turn our attention to some of the key directories and configuration files within JBoss.

JBoss 2.4.5 has a well-defined directory structure that makes it easy to manage and navigate. Table 3-1 describes each directory.

Table 3-1: JBoss 2.4.5 Directory Structure

Table 3-1

Changing the Configuration Directory

JBoss comes bundled with a complete set of default configuration files. These files are all located under the conf/default directory and are meant to give a quick start into the JBoss world.

It is a good idea to create a custom configuration directory for an application or application suite (set of applications). This technique is useful when different application suites will each run its own instance of a JBoss server.

Each JBoss instance will use a single configuration set. The application for this article will use configuration files located in the conf/oci_security directory.

How does JBoss know which directory to read from?

The JBoss server's main entry point is through the class org.jboss.Main. JBoss provides the following scripts to start the server:

org.jboss.Main accepts as its first argument a directory name that is relative to the conf directory. By default JBoss uses the name default, thus the conf/default directory.

The line below shows the updated run script.

java -classpath "%JBOSS_CLASSPATH%" org.jboss.Main oci_security

To verify that JBoss is using your configuration directory, examine the server console output after the server has been started. You should see the following line near the top of the console.

Using configuration "oci_security"

JNDI

In a distributed computing environment, such as J2EE, we need a standard mechanism to locate resources from remote servers. This type of service is best known as a naming service.

In the Java world, we think of this as JNDI. JNDI is an abstraction that allows for resources to be stored and retrieved from a well-known location using a name (String).

The JNDI name is sometimes referred to as a context name. The only important point to understand for this article is that we use a name to locate a resource or service. Please see References for links to JNDI resources.

JBoss Specific Deployment Descriptor (jboss.xml)

As with any J2EE server, you will have to create vendor-specific deployment descriptors.

JBoss uses a deployment descriptor called jboss.xml that maps EJB names to a JNDI name, along with numerous other configuration options, such as defining the Security Domain. The jboss.xml file is deployed with the application.

To specify the JNDI name for the CatalogMgrBean, we simply take the EJB name from the ejb-jar.xml file and make a mapping in the jboss.xml file. Then we add a tag named  that specifies how to locate the bean.

Diagram 3-1 shows how to map a bean into a JBoss JNDI context.

Diagram 3-1: Mapping EBJ's to JBoss

Diagram 3-1

We will come back to the jboss.xml file when we discuss how to configure a security domain.

JBoss Server Configuration File (jboss.jcml)

The jboss.jcml file is an XML file containing the configurations of all JMX MBeans (services) that JBoss will use to run an instance of the server. This file is proprietary to JBoss.

We are going to focus on configuring the following services:

  1. Database Services (MySQL)
  2. JBoss Security Manager Services
  3. SRP Security Services

1. Configuring JBoss to use the MySQL Database

Configuring a database in JBoss is fairly straightforward. The JdbcProvider MBean is used to tell JBoss which JDBC drivers to load.

  1. <mbean code="org.jboss.jdbc.JdbcProvider" name="DefaultDomain:service=JdbcProvider">
  2. <attribute name="Drivers">org.gjt.mm.mysql.Driver</attribute>
  3. </mbean>

Table 3-2: JdbcProvider Configurable Attributes

Attribute ValueDescription
Drivers Comma-separated list of JDBC Driver classes.

The only configurable attribute for the JdbcProvider MBean is Drivers. This is a comma-separated list of JDBC driver classes.

The above configuration tells the JdbcProvider MBean to use the MySQL driver. The driver is loaded when the server starts.

The MySQL driver class is located under the JBoss lib/ext directory in the mm.mysql-2.0.4-bin.jar file. This JAR file should have been copied to the lib/ext as part of this article's application installation process.

The XADataSource MBean manages database connections using a javax.sql.DataSource implementation.

DataSource is a factory for retrieving connections. There are more configurable attributes than shown below. This configuration is just enough to get JBoss talking with a MySQL database. Please see References for links to JBoss resources.

  1. <mbean code="org.jboss.jdbc.XADataSourceLoader"
  2. name="DefaultDomain:service=XADataSource,name=DefaultDS">
  3. <attribute name="PoolName">DefaultDS</attribute>
  4. <attribute name="DataSourceClass">
  5. org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl
  6. </attribute>
  7. <attribute name="URL">jdbc:mysql://localhost/security</attribute>
  8. <attribute name="JDBCUser"></attribute>
  9. <attribute name="Password"></attribute>
  10. </mbean>

Table 3-3: XADataSourceLoader Configurable Attributes (Partial List

Attribute ValueDescription
PoolName The JNDI name assigned to the pool. The name is bound under 'java:/name'. For example, 'java:/DefaultDS'.
DataSourceClass A fully qualified class name of the javax.sql.XADataSource implementation. JBoss provides a proxy implementation if a JDBC driver does not support an XADataSource. The implementation is org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl.
URL The JDBC URL to locate the database.
JDBCUser The user to connect to the database as. When connecting to MySQL from the localhost this attribute is not required.
Password The password to connect to the database as. When connecting to MySQL from the localhost this attribute is not required.

The database is located on the localhost and has a name of security. Recall that the database was created with the name security.

Refer to the downloaded resource bundle for details on how to create the database.

Configuring JBoss Security Manager Services

JBoss comes with a default Security Manager MBean called JaasSecurityManagerService. The default implementation is usually sufficient for most applications, including ours.

The JaasSecurityManagerService MBean provides a mechanism to abstract the underlying JBoss Security Manager service. Thus simply by changing the configuration, we can change how JBoss handles security.

By default the JaasSecurityManager MBean is used to provide server side JAAS authentication and authorization. Listed below is the default configuration for the JaasSecurityManagerService MBean.

  1. <mbean code="org.jboss.security.plugins.JaasSecurityManagerService"
  2. name="Security:name=JaasSecurityManager">
  3. <attribute name="SecurityManagerClassName">
  4. org.jboss.security.plugins.JaasSecurityManager
  5. </attribute>
  6. <attribute name="LoginConfig">Security:name=DefaultLoginConfig</attribute>
  7. </mbean>

Table 3-4: JaasSecurityManagerService Configurable Attributes (Partial List)

Attribute ValueDescription
SecurityManagerClassName Specifies the name Security Manager implementation class. By default JBoss uses org.jboss.security.plugins.JaasSecurityManager to provide JAAS security.
LoginConfig Specifies the object name of the MBean service that is used to locate the server side security configuration file.

The JaasSecurityManagerService MBean also provides a mechanism to handle the JNDI mapping of a Security Domain.

A Security Domain specifies an application-wide security manager whose responsibility is to ensure that users are both authenticated and authorized to access J2EE resources. This is exactly what the JaasSecurityManager MBean provides. The listing below shows how to configure JBoss with a Security Domain.

  1. <jboss>
  2. <security-domain>java:/jaas/OciSRP</security-domain>
  3. <enterprise-beans>
  4. <session>
  5. <ejb-name>UserMgrBean</ejb-name>
  6. <jndi-name>ejb/UserMgrHome</jndi-name>
  7. </session>
  8. <session>
  9. <ejb-name>CatalogMgrBean</ejb-name>
  10. <jndi-name>ejb/CatalogMgrHome</jndi-name>
  11. </session>
  12. </enterprise-beans>
  13. </jboss>

The above listing is the same jboss.xml file that we saw in section one and earlier in this section with the addition of the <security-domain> attribute.

This attribute tells JBoss the JNDI name to use when looking up the security manager. The name 'OciSRP' also plays a role as the server side auth.conf JAAS application name. Recall that there are two JAAS auth.conf files. One file is for client-side JAAS processing (logging in) and the other is for server-side JAAS processing. The details of server-side JAAS are coming in just a bit.

The DefaultLoginConfig MBean is used to locate the server-side security configuration file, which in our case is the JAAS auth.conf file.

This MBean is referenced by the JaasSecurityManagerService MBean. The JaasSecurityManagerService MBean configuration listed above used the attribute LoginConfig to specify the DefaultLoginConfig MBean object name, Security:name=DefaultLoginConfig. The server side auth.conf file is relative to the configuration directory, which is conf/oci_security.

  1. <mbean code="org.jboss.security.plugins.DefaultLoginConfig"
  2. name="Security:name=DefaultLoginConfig">
  3. <attribute name="AuthConfig">auth.conf</attribute>
  4. </mbean>

Table 3-5: DefaultLoginConfig Configurable Attributes

Attribute ValueDescription
AuthConfig Specifies the auth.conf file name. This name is relative to the configuration directory (conf/oci_security).

Configuring JBoss Secure Remote Password (SRP) Services

In a nutshell, SRP is an open-source implementation of a strong public key exchange handshake. Please refer to Resources for links to SRP.

Luckily we do not have to understand how SRP truly works because JBoss provides an implementation of the SRP protocol.

Now there is one hitch to the out-of-the-box implementation, and that is, at the time of this writing, there is no way to tell the SRP implementation to look up authentication data (usernames and passwords) from a database. The SRP implementation provided by JBoss reads authentication data from a file. The desire to read from a database is the motivation for creating a new MBean to provide the functionality our application requires.

Using the SRP implementation provided by JBoss is as simple as configuring a couple of MBeans.

The first MBean we will look at is the SRPService MBean followed by the custom SRPDatabaseVerifierStoreService MBean. These MBeans, along with other low level services, make up the SRP Authentication Server.

The SRPService MBean is responsible for maintaining the SRP Authentication Session Cache and exposing a Remote Method Invocation (RMI) client. This cache holds authentication information for consecutive calls to secure J2EE resources. The listing below shows the configuration for the SRPService MBean.

  1. <mbean code="org.jboss.security.srp.SRPService"
  2. name="Security:service=SRPService">
  3. <attribute name="VerifierSourceJndiName">srp/OCIVerifierStore</attribute>
  4. <attribute name="AuthenticationCacheJndiName">srp/AuthenticationCache</attribute>
  5. <attribute name="JndiName">srp/SRPServerInterface</attribute>
  6. </mbean>

Table 3-6: SRPService Configurable Attributes (partial list)

Attribute ValueDescription
JndiName Specifies the JNDI name of the SRPService MBean. The default name is 'srp/SRPServerInterface'.
VerifierSourceJndiName Specifies the JNDI name of the MBean responsible for looking up SRP password information.
AuthenticationCacheJndiName Specifies the JNDI name of the authentication cache MBean. The default cache is located using the name 'srp/AuthenticationCache'.
AuthenticationCacheTimeout Specifies the timeout in seconds for the authentication cache. The default is 30 minutes (1,800 seconds).
AuthenticationCacheResolution Specifies the interval between checks for timeouts. The default is one minute (60 seconds).

It is important to understand that when the Authentication Cache times out, all authentication data is lost. Thus calls to secure J2EE components will fail.

As of this writing, JBoss does not provide a way for renegotiating SRP authentication information. The easiest way to handle this is to set the timeout period to be 50 years, for example. If this is inappropriate then you will have to handle the renegotiation. One way could be to write your own SRP Authentication Cache MBean.

The com.ociweb.jmx.jaas.srp.SRPDatabaseVerifierStoreService is a custom MBean written specifically for this article's application. Recall that JBoss, as of this writing, does not have a SRP MBean that can retrieve authentication information from a database. The listing below shows the configuration for the SRPDatabaseVerifierStoreService MBean.

  1. <mbean code="com.ociweb.jmx.jaas.srp.SRPDatabaseVerifierStoreService"
  2. name="Security:name=OciSecurityService">
  3. <attribute name="JndiName">srp/OCIVerifierStore</attribute>
  4. <attribute name="DsJndiName">java:/DefaultDS</attribute>
  5. <attribute name="PasswordQuery">
  6. SELECT TRIM(password) FROM users WHERE username=?
  7. </attribute>
  8. </mbean>

Table 3-7: SRPDatabaseVerifierStoreService Configurable Attributes

Attribute ValueDescription
JndiName The JNDI name used by the SRPService MBean to locate this service.
DsJndiName Specifies the JNDI name for the Data Source of the database containing User information (username and password).
PasswordQuery A SQL query that is used by this MBean to locate a password based on a 'username'. The SQL statement must accept one and only one parameter that uniquely identifies a user, if it exists. This means that the query should return zero rows if the username did not exist, or one row containing the username's password.

To retrieve authentication information the SRPDatabaseVerifierStoreService MBean uses an implementation of the org.jboss.security.srp.SRPVerifierStore interface called com.ociweb.jmx.jaas.srp.DatabaseVerifierStore.

The DatabaseVerifierStore is the object responsible for retrieving the password from the database. This is done by using the SRPDatabaseVerifierStoreService MBean's PasswordQuery attribute value.

The DatabaseVerifierStore creates and returns a SRP verifier that will be used during the client-server SRP authentication process.

The only step left is to configure the server side auth.conf file to use JAAS implementations of SRP Login Modules. We will look at how this is done in the next section.

Authenticating a User

The best way to understand how server-side JAAS authentication works is to walk through a code example.

Remember that the process of authenticating a Subject means that we verify he is who he says he is, and if successful, associate a new Principal to the Subject. Refer back to section two for information on JAAS.

We will look at code snippets from the point of view of a unit test (JUnit) and assume that 'Brian' (that's me) is trying to login.

Table 3-8: User, Groups, and Roles ('Brian')

UserGroupRoles
Brian Manager ManageUsers, UpdateCustomer

The Client Side

The diagram below shows the first step in the authentication process.

Diagram 3-2: Client Side JAAS Login

Step 1 in the Authentication Process

The UsernamePasswordHandler provided by JBoss provides a very convenient CallbackHandler for unit testing because the username and password can be hardcoded.

In a real application, you would create a CallbackHandler that would solicit for a username and password via a user interface or utilize a biometric device (retinal scanner, thumbprint reader, etc).

A LoginContext is instantiated by the client application. The LoginContext constructor accepts a JAAS Configuration Application name ('srp') and a CallbackHandler. The JAAS Configuration Application name ('srp') maps to a name in the client side auth.conf file. This tells the LoginContext which LoginModules should be invoked.

The LoginContext can use the -Djava.security.auth.login.config Java Virtual Machine (JVM) argument to locate the auth.conf file. The snippet below is from the build.xml file that comes with the sample application.

-Djava.security.auth.login.config=file://${jboss.home}/client/auth.conf

There is nothing inherently special about having the file located in the $JBOSS_HOME/client directory. This is just a convenient location.

The LoginModules defined in the above auth.conf file are supplied by JBoss.

The first LoginModule invoked is org.jboss.security.srp.jaas.SRPLoginModule. This LoginModule is responsible for initiating the client-server SRP authentication process for a given username and password.

The details of the client-server SRP authentication process are discussed in just a bit.

If client-server SRP authentication succeeds then the second LoginModuleorg.jboss.security.ClientLoginModule is invoked. The ClientLoginModule does not perform any authentication, but rather binds the JAAS Subject and credential, populated by the SRPLoginModule, to the JBoss server. This is an example of LoginModule stacking that was discussed in section two.

Listed in Table 3-9 are two important options for the ClientLoginModule.

Table 3-9: ClientLoginModule Options

Option NameValid ValuesDescription
password-stacking Any non-null value. Recommended value is 'useFirstPass'. If enabled then Subject and Credential information should have been set by a LoginModule higher in the stack.
multi-threaded 'true' or 'false' If 'true' then each client thread is able to store authentication information on the server. This allows for multiple users to login. If 'false' then only one user may be logged in to the server.

The configuration outlined above uses password-stacking. This allows for the SRPLoginModule to perform the authentication and the ClientLoginModule to bind the Subject and credential to JBoss. A multi-threaded server is used to allow concurrent users to login to the server at once, which is probably the normal mode of operation.

The credential that is stored is not the password, but rather a verifier. A verifier is mathematically similar to a public key, where the verifier is easily computed from a password, but reverse engineering the password from the verifier is infeasible. The client passes the verifier to the server any time authentication needs to be verified with the server. We will see this when 'Brian' tries to access our secured EJBs.

The Server Side

When the client side LoginModule org.jboss.security.srp.jaas.SRPLoginModule is invoked, a lookup of the SRP Authentication Server is performed. The SRP Authentication Server is located by the srpServerJndiName attribute value. The srpServerJndiName tells the client SRPLoginModule where to locate the SRP Authentication Server.

Diagram 3-3 shows the SRPService MBean configuration and how the client auth.conf file maps the srpServerJndiName JNDI name.

Diagram 3-3: SRPService Configuration

SRPService Configuration

The client SRPLoginModule uses the JndiName attribute value to locate the SRPService MBean and then begins the authentication process.

The client SRPLoginModule first requests a set of SRP parameters based on the given username. Included in the SRP parameters is the verifier based on the actual password.

Recall that the verifier is created by the SRPDatabaseVerifierStoreService.

The client and server SRP LoginModules will negotiate several times to ensure that the authentication is indeed authentic.

If the client and server SRP LoginModules agree the user is who he says he is, the authentication succeeds. Upon success, a server-side JAAS Subject, Principal and verifier are created for managing secure J2EE calls. Once authentication succeeds the calling client is allowed to try accessing secure J2EE resources.

The truly impressive aspect of J2EE JAAS authorization is that just a few lines of client code and a few configuration files is all it takes to build a powerful security framework.

The next step is to access secured J2EE resources.

Authorizing Access to J2EE Resources

Brian is successfully logged into the application.

Now it's time for Brian to start accessing secured J2EE resources, namely the CatalogMgrBean and UserMgrBean. Recall that the authorization process ensures that the calling client has permissions to access a secure resource.

When a client obtains a reference to a secure remote EJB and invokes a method on that bean, the method invocation passes the following information to the server:

When JBoss receives an incoming method invocation, it will determine if the bean is configured with a Security Domain. Recall that a Security Domain specifies an application-wide security manager whose responsibility is to ensure that users are both authenticated and authorized to access J2EE resources.

JBoss has a set of Interceptors that are executed each time a method invocation hits the server. One of those Interceptors is the Security Interceptor. If the bean is not configured with a Security Domain, the Security Interceptor passes the method invocation to the next Interceptor. If the bean is configured with a Security Domain, the Security Interceptor will perform the following two tasks.

  1. Perform a server side JAAS login to authenticate the user
  2. Authorize the user (map user roles to ejb-jar.xml file)

Performing a JAAS login on the server side during a method invocation may seem redundant, since the calling client has already been authenticated during the client login process. This is somewhat true for the SRP LoginModule implementation, but there are no rules stating that a user must be authenticated on the client side.

Some client side authentication technologies simply bind the username and password to the server, and upon accessing a secure resource, perform authentication. These types of technologies do not provide a robust security model, but they do exist.

In any case the server needs to perform a JAAS login again to obtain the necessary JAAS objects (Subject, Principals, etc).

Diagram 3-4: Server Side auth.conf

Server Side auth.conf

Recall that the SRPService MBean is configured with a SRP Authentication Cache. This cache is used during the server-side JAAS login process to improve performance.

The server side LoginModule org.jboss.security.srp.jaas.SRPCacheLoginModule provides a door into the SRP Authentication Cache.

Table 3-10: SRPCacheLoginModule Configurable Attributes

Attribute ValueDescription
cacheJndiName Specifies the JNDI name of the Authentication Cache. This should be the same JNDI name used in the SRPService MBean configuration.

The SRPCacheLoginModule will look up the authentication information from the cache and make that information available to all other LoginModules on the stack.

The next LoginModule to be executed, according to the server side auth.conf file, is com.ociweb.jmx.jaas.srp.DatabaseRoleLoginModule.

This is a custom LoginModule written to work with the SRPCacheLoginModule. The DatabaseRoleLoginModule does not attempt any authentication, but rather returns the authentication information available on the authentication stack.

The real power of this LoginModule lies in its role mapping behavior. Role mapping is the process by which all roles are retrieved for a single user and mapped to roles in the deployment descriptor (assembly-descriptor section). Thus role mapping provides the functionality of ensuring that a user has the privileges to access a J2EE resource.

Table 3-11: DatabaseRolesLoginModule Configurable Attributes

Attribute ValueDescription
dsJndiName Specifies the JNDI name for the data source of the database containing the logical roles (Principals).
rolesQuery A SQL statement that returns zero or more rows of data representing the role name and the role group for a single user (username). When JBoss performs role mapping, it uses a group called 'Roles'. Thus we want to put all returned roles into a group called 'Roles'.

If the user is authenticated, a JAAS Subject is created that contains a Principal representing the user's identity (Brian) and a java.security.acl.Group called 'Roles' that contains a collection of role names assigned to the user.

These roles are retrieved by the DatabaseRoleLoginModule using the rolesQuery SQL statement. The Group object is used by JBoss to hold the roles used in the Role Mapping process.

Diagram 3-6 shows the mapping between the deployment descriptor and the roles associated with Brian. In this case we configured the UserMgrBean.findUser(int) to only allow access to those with the 'ManageUsers' role. Section one covers the details of creating the security model.

Diagram 3-5: Valid Roles (Deployment Descriptor Mapping)

Diagram 3.5: Valid Roles (Deployment Descriptor Mapping)

As you can see from the above diagram Brian has the role 'ManageUsers'. Thus Brian will be allowed to execute the method.

Now let us see at what happens when Brian tries to access a resource that he does not have authorization for.

Diagram 3-7 shows that this time Brian is trying to access the UserMgrHome.getDemographicInfo() method. In this case Brian does not have the correct roles, and access is denied by throwing a java.lang.SecurityException.

Note that this exception is wrapped in a java.remote.RemoteException. If your client code needs to handle a failed access attempt, then simply perform a recursive call on Throwable.getCause() to retrieve the root cause of the exception. This holds true for any RemoteException thrown by a J2EE server.

Diagram 3-6: Invalid Roles (Deployment Descriptor Mapping)

Diagram 3.6 Invalid Roles (Deployment Descriptor Mapping)

Summary

This article covered a lot of ground. We have seen how to create a robust data model to hold user and role information, along with an overview of the JAAS framework, and finally wrapped things up by configuring JBoss to deploy and secure our application using SRP.

J2EE JAAS security provides a powerful framework to secure resources without writing a lot of custom functionality. As enterprise data becomes more and more secure, we need the ability to change authentication technologies without jumping through a lot of hoops. By utilizing J2EE and JAAS, we are able to keep up the pace of creating and securing robust, cutting-edge, enterprise applications.

Resources

Resource Bundle

The resource bundle contains example JAAS LoginModules and CallbackHandlers, along with a completely configured JBoss server using the SRP protocol discussed in section three.

Scripts and Ant targets are also available in the resource bundle to install all third-party software (MySQL, JBoss, Ant, JUnit). Simply download and follow the instructions provided in the readme.txt file.

Download Resource Bundle

Open Source Tools

References

Books

Links



Software Engineering Tech Trends (SETT) is a monthly publication featuring emerging trends in software engineering.

Check out These popular articles!

secret