Using JAAS for Authorization and Authentication

Dan Moore (jaaspaper AT mooreds full stop com)

If you like this paper, you may like my weblog

Seurat Company, nee XOR, Inc.

Table of Contents
1. Introduction
1.1. Who should read this
1.2. Definitions
1.3. Conventions
2. Authentication
2.1. Login Module
2.2. Module Configuration
2.3. Integrating Authentication Into Struts
3. Authorization
3.1. Permissions
3.2. Access Control Policy Configuration Files
3.3. Integrating Authorization into Struts
4. Conclusion
5. Thanks
Bibliography

1. Introduction

This paper explains how to use the Java Authentication and Authorization API (JAAS). It plugs JAAS into the Struts framework. Though this paper focuses on Struts, and in particular the example application distributed with Struts, the lessons learned should be applicable to any MVC web framework.

In addition, while there are many articles on authentication with JAAS [1], [2] , using the API for authorization is relatively undocumented [3]. This paper will show how to use JAAS to secure resources in an MVC architecture.

There are two points of integration with Struts. During the login process and when the client requests a resource from Struts (which will usually be a URL). At each of these points, the application should defer to JAAS classes to perform the action.

This paper will first examine the JAAS infrastructure and then explain how the integration outlined above took place. If you just want to see the code, download it from here.


1.1. Who should read this

This article is for developers who are relatively familiar with web applications and the java security framework. A sample application in Struts is used for the examples [4], but familiarity with Struts is not required. For information on java security, check out Java Security by Scott Oaks. For information on web applications, check out Sun's web site [5]. For information on Struts, check out the Struts web site [6].


1.2. Definitions


2. Authentication

2.1. Login Module

The login module receives information about the user and authenticates the user, thereby verifying that he or she is a valid subject. There are several implementations of login modules currently available. If a custom login module is needed, resources are available [8].

Login Module implementations

  • Sun provides default implementations for Solaris and NT.

  • Tagish has several implementations licensed under the GPL [9].

  • JBoss provides several implementations [10].

Note: For WebLogic 6.1 and Tomcat 3.2, both the jaas.jar and the login module classes needed to be in the non webapp specific classpath.

These login modules are identified by a name in a configuration file and then called by a LoginContext class that JAAS provides. The LoginContext class does not appear to be thread safe--the javadoc states that " a LoginContext should not be used to authenticate more than one Subject. A separate LoginContext should be used to authenticate each different Subject" [11]. Most of these modules expect to be run from an application or on the command line, and thus to be able to interact directly with a user. Since a web application resides on a server, it may be necessary to write an adapter to pull the needed user information from where the web application stores it and place it in a form that the login module can process.


2.2. Module Configuration

In addition to putting classes that can do authentication in the correct classpath location, some configuration is required to let the JAAS classes know what type of authentication is possible. JAAS allows complex login schemes [12], however, for most web applications, such complexity is not required. Typically, a username and password will be read from a form, and compared against known server side values. An authentication scheme which verifies a username and password combination against a server side file will be examined below.

Example 1. JAAS login module configuration file


FileLogin
{
            com.tagish.auth.FileLogin required debug=true \
            pwdFile="/usr/local/tomcat/webapps/struts-example/WEB-INF/passwd";
};

In Example 1, the FileLogin authentication scheme has one required module. FileLogin implemented by the com.tagish.auth.FileLogin class. Both the class name and a token indicating the relationship of the module to the scheme are mandatory. Here the required token indicates that the FileLogin module must validate this login or the scheme as a whole fails. It is also possible to pass additional information to the login module in this configuration file. This module needs to know where its password file is located.

Since this configuration file may be located anywhere on the file system, the authentication classes need to be informed where this file is. There are two ways to do this: adding parameters to a JVM wide general configuration file (java.security), or passing the information in on the command line when starting the JVM.


2.3. Integrating Authentication Into Struts

After the login module is recognized by JAAS, hooks into the web application need to be written. In the example application, an Action class takes a username and password from its Form class and authenticates the user against a database. This is the logical place to hook in the JAAS authentication module.

The Struts example application has a LogonAction class. Initially, the relevant portion of this class looks like this (note that this code is unchanged except to break up lines for ease of printing, as detailed in Section 1.3).

This approach works just fine for a sample application. The LogonAction class does all of the authentication in the perform method. But the LogonAction class should be a thin layer deferring the actual work to business objects. In addition, switching the source users are authenticated against requires changes to this class since it is hard coded to expect a password and username. Also, as mentioned in Section 2.1, using the JAAS authentication module will be difficult, as most implementations expect some level of user interaction. Putting a business object between the Struts application classes and the JAAS classes can make life easier.

Example 5. Modified LogonAction Authentication


   125          String username = ((LogonForm) form).getUsername();
   126          String password = ((LogonForm) form).getPassword();
   127          Hashtable database = (Hashtable)
   128            servlet.getServletContext().getAttribute(Constants.DATABASE_KEY)
   129          if (database == null)
   130              errors.add(ActionErrors.GLOBAL_ERROR,
   131                         new ActionError("error.database.missing"));
   132          else {
   133              Auth fa = new com.xor.auth.FileAuth(username,password);
   134              if (fa.authenticate()) {
   135                  user = (User) database.get(username);
   136                  HttpSession sess = request.getSession();
   137                  sess.setAttribute(Auth.SUBJECT_SESSION_KEY, fa.getSubject());
   138              }

Lines 125-132 are the same in this example as they were in Example 4. However, rather than the LogonAction class performing the authentication, it defers to a special Auth class. Note that on line 135, the database is still used to get the User object, since the sample application used it in other code.

Auth is a custom interface specifying the contract that the adapter for the JAAS classes must fulfill to interface with web applications in general and this example application in particular. FileAuth is an implementation of that interface which adapts the com.tagish.auth.FileLogin login module specified in Example 1.


3. Authorization

3.1. Permissions

Permissions are the heart of authorization; they control access to resources. However, the JAAS permissions are built on top of the existing java security model. This model is very good for controlling access to resources like sockets and files, but has no concept of URLs. Thus, to apply JAAS to a web application, a new permission class must be created.


3.1.1. Create permission class

A custom Permission class that understands URLs must be created. There are two ways to do this.

Extending java.security.BasicPermission is one option. Using this would tie permissions to literal URLs (e.g, one could say that the admin principal should have access to the /admin/index.do page). However, the other option would be better: to create a URLPermission class extended the java.security.Permission class and handled wild cards in a manner similar to the java.io.FilePermission class [15]. Then, one could say that the admin principal should have access to the /admin/ directory and all resources below it.

In either case, since URLs are read only, there is no need for any of these permissions to have an action attribute. On the other hand, it may be useful to specify an attribute of a Permission to determine whether a given URL may only be viewed over a secure connection. For more on extending Permissions in novel manners, see the interesting IBM article titled Extend JAAS for class instance-level authorization [16].

However, since this is a proof of concept, a BasicPermission implementation is fine. For a production system, subclassing Permission would be required.


3.2. Access Control Policy Configuration Files

The default configuration for JAAS permissions looks much like the configuration for normal java security. Java security entries consist of a listing of permissions. The permissions can be limited to either a specific code base, or code that has been signed by a specific person, or both. JAAS permission listings can have none, one or both of these elements. However, at least one Principal association is required.

At times there will be principals that are granted a superset of the permissions granted to other principals. For example, in Example 8, subjects with the admin principal can view all resources that subject with the user principal can. However, the /struts-example/editRegistration.do URL is listed twice (once in the user section and once in the admin section). It would be nice to be able to avoid duplicating permissions and delineating this relationship between principals, but this is not possible. While more than one principal can be listed for a given set of permissions, if that happens, "[t]he current Subject running the code must have all of the specified Principals in its Principal set to be granted the entry's Permissions" [17]. However, this problem may be dealt with by granting all users with the admin Principal the user Principal as well.

On the other hand, it is possible to have permissions be supersets of other permissions. The implies method of the Permission class can be overridden to provide this behavior. For example, there could be an admin Permission which could imply all other URL permissions.

Note: Example 8 is an example of a file based implementation of the policy. It can be replaced with a RDBMS implementation by changing the value of the auth.policy.provider variable in the java.security file.

Since the policy file may be located anywhere, JAAS needs to be told where to look for it (this problem is similar to that faced by the authentication configuration setup detailed in Section 2.2. There are two ways to do this: adding parameters to a system wide general configuration file (java.security), or passing the information in as a command line option when starting the JVM.

Note: With WebLogic 6.1, only the command line method worked. Tomcat 3.2 seemed to work with both methods.


3.3. Integrating Authorization into Struts

Now that permissions have been set up and JAAS knows about them, it is time to actually apply them in the context of the application. Every java class that accesses a potentially security sensitive resource needs to check to see if the SecurityManager is running, and if so verify that the calling class is running in a context that allows the access.

The web application needs to perform a similar check for each URL that the user is trying to view. In an MVC architecture, the logical place to put such access control is the controller. In Struts, the ActionServlet is the controller. For the sample application the controller class, org.apache.struts.action.ActionServlet, was subclassed, and the process method was overridden.


3.3.1. Installing Access Control Logic into the ActionServlet

First of all, it is important to note that when the ActionServlet is subclassed to provide authorization services, only resources that the ActionServlet controls are protected. Thus, in the example application, only Actions are protected. In a real application, it'd be better to have two methods of access control:

Both of these should delegate to a business class that does the actual security check.

There are at least two pages whose view should not be protected at all: the login page and the login error page. In addition, there are certain security attributes that are peculiar to web applications; for example, some users may need to have all interaction over an SSL connection. The controller needs to do at least three things to provide authorization services for a web application.


3.3.2. The ActionServlet Subclass

Example 12. The Overridden process Method


     1	   protected void process(HttpServletRequest request, \
            HttpServletResponse response)
     2	   throws ServletException, java.io.IOException
     3	   {
     4	       String loginPage = request.getContextPath()+"/logon.do";
     5	       String pageReq = request.getRequestURI();
     6	       Permission perm = \
                PermissionFactory.getInstance().getPermission(pageReq);
     7	       Subject subject = \
                ((Subject)(request.getSession().getAttribute( \
                    Auth.SUBJECT_SESSION_KEY)));
     8	       if (subject == null && \
                (! request.getRequestURI().equals(loginPage))) {
     9	           // redirect to login page
    10	       } else if (subject == null &&  \
                request.getRequestURI().equals(loginPage)) {
    11	           // login page is always permitted
    12	            super.process(request,response);
    13	
    14	       } else {
    15              if ( ! AuthUtils.permitted(subject, perm) ) {
    16  	           // subject is not permitted; redirect to error page
    17  	    } else {
    18	                super.process(request,response);
    19	            }
    20	        }
    21	   }

Lines 6 and 7 get needed information including the Permission that represents the URL the user is trying to access (PermissionFactory is a factory class that returns the appropriate Permission), and the cached Subject.

On lines 8-14, the case of the unauthenticated user is handled by redirecting the user to the login page, where access is always allowed (line 12). The reason that this can't be handled by the wildcard grant in Example 8 is that the Subject is null in this case. If the program catches this and creates a new Subject, that Subject still has no Principals and thus is still denied access. For a production system, it's conceivable this case could be handled with an anonymous Principal.

If this is a request for any other resource than the login page, on line 15 the access control class is called. Access is either disallowed, or the process method of the superclass is called.

The ActionServlet should not know about the particulars of the access control; this should instead be handled by a business class. For this example, such a business class was written; it is examined in the next section.


3.3.3. The Access Control Class

This class is similar to the java.security.AccessController class [19], and ends defers to that class eventually (via the SecurityManager). The logic in the class can then be reused anywhere that authorization is needed. The class will basically check whether a Subject has a given Permission.

Lines 10-15 create a SecurityManager. If the application happens to be one of the rare java applications that run with a SecurityManager installed, then that SecurityManager should be used. If not, a new one is created to check the permissions on line 12.

Lines 16-23 do the actual permission check. If the Subject does not have a Principal which has been granted the Permission, the SecurityManager will throw an exception. For reasons of cleanliness and clarity, this exception is caught and converted to false, which is returned to the calling class. Otherwise, the action is allowed, and true is returned.

The null on line 22 is very important; it tells the SecurityManager to consider this resource access in an isolated context, ignoring the permissions of code currently on the execution stack. For further information, see chapter 5 of Java Security.


4. Conclusion

The authentication piece of JAAS seems fairly bulletproof. The idea of pluggable authentication modules is great and the developer can leverage a number of existing modules to ease development.

Using JAAS to leverage the SecurityManager for authorization is entirely commensurate with the java security model. There are resources that only certain users with certain principals should be able to see. Rather than reinvent an access control layer, it makes sense to use the one that java already provides.

However, there are some caveats. This was an extremely simplistic example, and the reader will have noted the number of places where parts of the system need to be replaced to create a production system; these include a new controller, permission class, and policy implementation. In addition, this permission model does not map well to the concept of different protocols used to view a URL.


5. Thanks


Bibliography

Scott Oaks Java Security O'Reilly & Associates June 2001

Notes

[1]

http://www.theserverside.com/resources/articles/Pramati-JAAS/article.html

[2]

http://java.sun.com/j2se/1.4/docs/guide/security/jaas/JAASRefGuide.html#Sample

[3]

This document does discuss authorization

[4]

This application is located at webapps/struts-example.war in Struts 1.0.2 tarball.

[5]

http://java.sun.com/webservices/docs/ea2/tutorial/doc/WebApp.html

[6]

http://jakarta.apache.org/struts/

[7]

http://java.sun.com/j2se/1.3/docs/api/java/security/Principal.html

[8]

http://java.sun.com/security/jaas/doc/module.html

[9]

http://free.tagish.net/jaas/doc.html

[10]

http://www.jboss.org/online-manual/HTML/ch09s17.html

[11]

The LoginContext javadoc is here

[12]

This document explains some of the possible configurations.

[13]

http://java.sun.com/security/jaas/doc/api.html#AppendixA

[14]

http://java.sun.com/security/jaas/doc/api.html#AppendixA

[15]

http://java.sun.com/j2se/1.3/docs/api/java/io/FilePermission.html

[16]

http://www-106.ibm.com/developerworks/library/j-jaas/?n-j-442

[17]

http://java.sun.com/security/jaas/doc/api.html#Policy

[18]

http://java.sun.com/security/jaas/doc/api.html#AppendixA

[19]

http://java.sun.com/j2se/1.3/docs/api/java/security/AccessController.html