Accessibility
 
 
JRun Authentication
By Karl Moss
JRun Engineer
Allaire Corp.

This article explores the built-in authentication mechanisms found in Allaire's JRun 3.0. Servlet/JSP authentication was introduced in the Servlet API specification, version 2.2, which JRun 3.0 supports, and allows you to place authentication and role-based access control (authorization) on any resource delivered by the servlet container.

Servlet 2.2 Security

Chapter 11 of the Servlet API specification, version 2.2, from Sun addresses security issues for Web applications. The specification addresses two main points: authentication and authorization. Let's first define these terms:

Authentication is the process of gathering user credentials (user name and password) and validating them in the system. This typically requires checking the credentials against some user repository, such as a database or LDAP, and authenticating that the user is who he or she says he or she is.

Authorization is the process of making sure that the authenticated user is allowed to view or access a given resource. If a user is not authorized to view a resource, the servlet container does not allow access.

From a Web application developer's point of view, a certain collection of Web pages may be restricted to a certain type of user, such as a manager. In the past, a Web developer had to implement a custom security mechanism to collect the user credentials and then verify that the user could view the pages. Servlet containers that implement the Servlet 2.2 spec now handle these details for you. By placing security constraints on a group of resources in the deployment descriptor (web.xml), the servlet container will automatically gather user credentials, authenticate the user, and authorize that the user can view a given page.

Nuts and Bolts

As previously mentioned, authentication starts with defining resource constraints in the deployment descriptor (web.xml). The servlet specification defines the deployment descriptor as the file that "conveys the elements and configuration information of a Web application." Part of that configuration information is not only "what" resources should have limited access, but "how" that access is to be applied. Let's first take a look at defining the "how."

The specification defines an auth-method element, which tells the servlet container (JRun) how authentication is to take place--in other words, what mechanism should be used to gather the user credentials. The specification defines four mechanisms:

BASIC — This mechanism is an HTTP challenge/response as defined in the HTTP/1.1 specification. The server responds to the client with a status code of 401 (unauthorized), and the client browser prompts for a user name and password. After collecting the user name and password, the client sends the information back to the server for authentication. BASIC authentication is not a secure authentication protocol because the user password is transmitted with a simple Base64 encoding.

FORM — You, the Web application developer, create an HTML login form to be used to gather user name and password information. Once complete the contents of the form are submitted to the server for authentication. FORM authentication is not secure either, because the user password may be transmitted in clear text.

DIGEST — Much like the BASIC mechanism, the client browser prompts for a user name and password. Unlike BASIC authentication, the user password is transmitted in an encrypted manner, which is much more secure. Unfortunately, digest authentication is not currently supported by all browsers.

CLIENT-CERT — Authenticates users using a client certificate. This mechanism requires the user to posses a Public Key Certificate (PKC).

Of these four mechanisms, JRun 3.0 current supports BASIC and FORM. Note that if you are concerned about the security risks of transmitting user password information, you can always introduce additional protection by applying a secure transport mechanism (HTTPS) or using security at the network level (such as the IPSEC protocol or VPN).

The following is the relevant section of the deployment descriptor (web.xml) for specifying BASIC authentication:

<login-config>
 <auth-method>BASIC</auth-method>
 <realm-name>MySite</realm-name>

</login-config>
The realm-name element defines the name of the security realm that will be used when the user is prompted; if a realm-name is not specified, the realm-name becomes the current Web application name. Again, the client browser prompts for the user name and password; you have no control over how the user information is gathered.

If you want to use FORM-based authentication, which gives you complete control over how the login information is gathered, the deployment descriptor must have the following elements:


<login-config>
 <auth-method>FORM</auth-method> 
 <form-login-config>
  <form-login-page>/loginpage.htm</form-login-page> 
  <form-error-page>/loginerror.htm</form-error-page> 
 </form-login-config>

</login-config>
Note that you specify the name of the login page and the name of the error page if there are problems with authenticating the user (such as a bad user name or password). The trick here is that when creating the login page, you must adhere to the naming conventions defined in the specification for the form field names and the action. The user name must have the name j_username, the password must have the name j_password, and the action of the form must be j_security_check. Here's a simple example:

<html>
<body>

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

</html>
This is a just a simple HTML form; you can jazz up the form any way you want. Just remember that you must use the predefined names, and you cannot currently gather any other type of information (and have it recognized); just the user name and password.

Now that we've defined "how" users are to be authenticated, let's now focus on "what" resources the users should have limited access to. This is also defined in web.xml. You can place what is called a security constraint upon any number of resources and also specify what type of user (i.e. a user in a particular role) has access to the resource:

<security-constraint>
 <web-resource-collection>
  <web-resource-name>Financial Reports</web-resource-name> 
  <url-pattern>/financials/*</url-pattern> 
 </web-resource-collection>

 <auth-constraint>
  <role-name>manager</role-name> 
 </auth-constraint>
</security-constraint>
You can define any number of security-constraint elements, each defining the Web resources that should have limited accessibility. Part of the security-constraint is the auth-constraint element, which defines the role (or roles) that a user must be part of to gain access to the resource. The user repository must include a list of roles that each user belongs to so that the servlet container can validate the roles defined in the security-constraint. If the authenticated user is not in a role that has access to that resource, the container will not allow the resource to be viewed (JRun will return a status of 403 - Not Authorized).

Note: For further definitions and explanation of servlet security, please refer to the Servlet API version 2.2 (or higher) at http://java.sun.com/products/servlet or the Developing Applications with JRun manual (Chapter 38), which is installed with JRun.

Programming Security in Your Servlets/JSPs

Perhaps placing security constraints on individual resources does not give you the fine-grained control you would like. You can also use the Servlet API to include calls in your servlets/JSPs to determine who the current user is, and if they belong to a given role. This allows you to create content conditionally on the fly. The following methods are available from HttpServletRequest:

string getRemoteUser();
boolean isUserInRole(String role);
Principal getUserPrincipal();
getRemoteUser returns the current user name, or null if no user has been authenticated. isUserInRole returns true if the current user is part of the given logical role name (we'll visit what a logical role name in just a bit). getUserPrincipal returns a java.security.Principal object representing the current user, or null if no user has been authenticated.

What is a logical role? The Servlet API allows you to create a cross-reference of role names that you use programmatically (in your servlet) to the role names stored in the user repository. This cross reference is created in the deployment descriptor (web.xml again) with the definition of the servlet. The security-role-ref element allows you to define the role name that is hard-coded in your servlet as well as the actual role name used by the servlet container. This may seem a bit odd, but it allows you to deploy a servlet, without modification, into a container that uses different role names (as long as you know what those role names are). As an example, consider a servlet container that uses a role name of "MGR" for managers and a servlet that checks for the role of "manager." The servlet definition in web.xml would contain:

<security-role-ref>
 <role-name>manager</role-name>
 <role-link>MGR</role-link>

</security-role-ref>
The servlet container must perform this cross-reference when isUserInRole() is called:

if (request.isUserInRole("manager")) {
   … add some logic for manager's only
}
Note that isUserInRole() will return false if no user has been authenticated. It is also important to realize that the methods in the API for programmatic security will not cause a user to be authenticated. The authentication must be initiated by the user attempting to access a resource with a resource-constraint element.

Logging off

The Servlet specification does not address how to log users off once they have been authenticated. It is safe to assume that you can simply invalidate the current session to log a user off, but if you want your session to remain intact, you can remove the following JRun-specific attributes from the current session:

allaire.jrun.security.authenticationState
allaire.jrun.security.principal

JRun 3.0 Custom Authentication

We have already explored how users are authenticated and what resources should be restricted, but we did not really touch on "where" user information is stored. The Servlet specification does not address the physical attributes of the user database, nor does it define how the servlet container should access this information (whether it be file based, JDBC, JNDI, etc.). JRun's default implementation is to store the user information in a user property file, which can be modified using any text editor. While this is a great way to prototype applications, Allaire's JRun team realized that not only is a more powerful authentication scheme necessary, but also developers may already have a user repository that they may want to "hook" JRun into. For these reasons, JRun allows a custom authentication class to be plugged into the server. This allows any type of user authentication to be performed against any type of user repository that you may have. Note that JRun also provides a custom authentication mechanism that uses JDBC as well. Check Chapter 38 of Developing Applications with JRun for more information.

JRun provides a simple Hashtable-based example implementation, which can be found at docs/api/jrun/allaire/jrun/security/CustomAuthentication.html (under the JRun install directory). You should be able to read the fully annotated source code and implement your own authentication class if necessary. Again, Chapter 38 of Developing Applications with JRun is a great reference for creating your own authentication class.

If you are going to implement your own custom authentication scheme, I have an implementation suggestion. It is not uncommon to store additional information in a user repository, such as personalization settings. Instead of rereading the user repository each time you need this information, you should consider extending allaire.jrun.security.AuthenticatedPrincipal with your own class, store your custom information here, and return an instance of this new class once a user has been authenticated. Then, when you need access to your custom information, you can simple call request.getUserPrincipal in your servlet/JSP, cast the returned object to your new class, and away you go!

About the author

Karl is a Principal Software Developer at Allaire Corporation where he works on the JRun Application Server. Karl is also the author of Java Servlets (McGraw-Hill) and Java Database Programming with JDBC (Coriolis), both of which are currently in their second edition. When not writing Java code you may find him playing his vintage video arcade game "Tutankham," for which he at one time held the National high score.