Chapter 7. Tomcat Realms

Sometimes, you need to protect your web pages and/or web applications, so only registered users can get access to them. To most end-users, they understand this as the login page of the website. To some technical professionals, this is known as the authentication facility.

A realm is a collection of pages, images and applications (collectively known as "resources") that is protected by a login or authentication method. For Tomcat, the realm mechanism is somewhat different from how most programmers understand it. Instead of a default servlet that handles the login, and having to set and check sessions explicitly for every page and servlet, Tomcat has a container security facility that does it for the programmer. Container-managed security enables the servlet or page author to leave out explicit session tracking in their code -- Tomcat handles the login and session tracking for them. When a user attempts to access a protected resource for the first time, he or she will be prompted automatically for his/her login credentials.

There are 4 types of authentication mechanisms provided by Tomcat "out-of-the-box":

If you configure for Basic Authentication, you will get prompted with a login dialog box when you attempt to access a protected resource. If you use Form-based authentication, your users will be redirected to a HTML page that allows them to login, when they attempt to access a protected resource. Custom authentication is used when you require additional information from the user before you allow him/her to login, and Digest authentication is used when you need an added level of security using hashed passwords.

For a more detailed explanation of container security, realms and authentication methods, please refer to Sun's servlet specifications.

7.1. Exercise Overview

The subject of security is actually a lot broader than most people think. In addition to authentication, there is also encryption, authorization, auditing, and a whole host of other components. For that reason, I'm not going to go into the details of security. If you are a system administrator, you're probably more interested in how to setup Tomcat for a particular security scheme than to argue the merits of different methods.

I will focus on just one particular security setup here. We will be setting up a JDBC realm on Tomcat 5 where the back-end database is Firebird v1.5, using the MyFirst web application we developed earlier. We will be using Form-based authentication for the login method.

Once you have worked through this example, I am hoping that you will also be able to infer the steps for your own particular setup.

7.2. JDBC Realm

This type of realm involves storing the credentials of your users (i.e. their usernames, passwords and assigned roles) inside a database. Tomcat will then need to be configured to use this database and the JDBC realm option inside the configuration files will need to be enabled.

Setting up a JDBC Realm involves the following steps:

  1. Install and Setup The Database

  2. Install the JDBC Driver

  3. Setup the Database Tables

  4. Test The JDBC Driver and Connection

  5. Deploy and Test the Web Application

  6. Prepare the Necessary Login and Error HTML Files

  7. Edit Tomcat's server.xml To Enable JDBC Realm

  8. Edit the Web Application's web.xml To Require Authentication

  9. Start Tomcat and Test

For this exercise, I shall be building on my Firebird v 1.5 for Linux article. You can use any database product you like, as long as it provides a JDBC driver (you could use an ODBC driver and a JDBC-ODBC driver, but you may want to re-consider this for production deployments). The SQL syntax may also differ, so you should expect some differences between my instructions and your own requirements.

7.2.1. Install and Setup the Database

To use the JDBC realm, you will need to have a database to store your user credentials. If you have not already done so, install a database, or use an existing database on your server or network. This document will not cover the installation of database products.

You will also need to ensure that your database can startup and shutdown, and you should be reasonably familiar with operations on the database you have chosen. I will not cover these aspects here.

If you are a Microsoft SQL Server user, I cannot help you. I do not use that product, and I hear that there are all sorts of problems accessing it with a JDBC driver. I personally prefer Firebird, because of its compact size and because it is free.

7.2.2. Install the JDBC Driver

As I mentioned earlier, you will need a suitable JDBC driver to connect to your database product. I say "suitable" because you could use a JDBC-ODBC bridge driver, but you would run into performance problems if your site becomes even moderately loaded. Also, a single database product may have several different vendors producing JDBC drivers for their platform. An example is Merant, which used to make very high quality JDBC and ODBC drivers for a range of database products. If there are several alternatives, choose a JDBC driver that fits your performance needs and price range.

For this exercise, I am using the Firebird database and the JayBird JCA/JDBC Driver for Firebird v1.5 beta3. This is a Type 4 JDBC driver, and though it's in beta, it actually works quite well.

You will need to copy the JDBC driver into $CATALINA_HOME/common/lib/ directory. In case you don't already know, the JDBC driver is normally a single jar or zip file. For example, I believe Oracle's JDBC driver is called classes.zip. For the Jaybird driver, it is firebirdsql-full.jar. You will need to refer to your JDBC driver documentation for the filename.

7.2.3. Setting Up The Database Tables

There are actually different ways you can setup the tables. You can even use existing tables of users, provided you know how Tomcat does the mapping. I will try to keep things simple here and build the requisite tables from scratch. You can see later, with the modifications to Tomcat's configuration files, how the mapping method can be inferred.

For this exercise, we will create two tables in the database, like the ones below, and populate them with data.

Table 7-1. tcusers Table - containing usernames and passwords

user_name [varchar(25) not null primary key] user_pwd [varchar(25) not null]
taylorsc taylorpass
isabellec isabellepass
daytonr daytonpass
azleaa azleapass

Table 7-2. tcroles Table - containing usernames and assigned roles

user_name [varchar(25) not null] role_name [varchar(20) not null]
taylorsc admin
isabellec admin
isabellec manager
isabellec tomcat
daytonr manager
daytonr tomcat
azleaa tomcat
primary key (username, rolename)  

I will not cover the SQL statements required. If you need hints, please read my Firebird article, which covers the creation of the two tables, but only for Firebird.

Notice that I specified 3 different roles here : admin, manager and tomcat. The admin and manager roles are built-in roles for Tomcat, that allow access to the Manager web application and the Tomcat Administration Tool. We can actually test if our JDBC realm works later by seeing if user "taylorsc" (who has the "admin" role) can login to the Administration Tool.

For this exercise, we will focus on the "tomcat" role, which we will use to determine if a user is authorized to access the resources in the realm.

7.2.4. Test The JDBC Driver and Connection

We can write a simple JDBC program to test the connection, to see if we can access the database.

7.2.5. Deploy and Test the Web Application

At this point, we should deploy and test our web application to verify its functionality before we put it in a realm and place a login form in front of it.

If you have been following along, and working through the exercises in this document, you should already have a HelloWorld servlet deployed in MyFirst directory, and it should work fine. We will use the MyFirst web application to "bootstrap" our JDBC realm.

You will recall that we created a "tomcat" role when we created and populated the database tables. We will use this role later to determine if a user has access to the HelloWorld servlet.

7.2.6. Prepare the Necessary Login and Error HTML Files

For this exercise, we will be using FORM-based

We will need 2 HTML files -- one for displaying the login page and one to show the user that he has keyed in the wrong password or username.

Here is the login page for this exercise:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Login Page</title>
</head>
<body>
<h1>Login to MyFirst</h1>
<p>
If you have been issued a username and password, key them in here now!
</p>
<form method="POST" action="j_security_check">
Username : <input type="text" size="15" maxlength="25" name="j_username"><br><br>
Password : <input type="password" size="15" maxlength="25" name="j_password"><br><br>
<input value="Login" type="submit">&nbsp;&nbsp;&nbsp;&nbsp;<input value="Clear" type="reset">
</form>
</body>
</html>

Note the form elements. The form action is "POST" and the action is "j_security_check", the username field's name is "j_username" and the password field's is "j_password". These elements MUST be included for the form to work.

Here is the error page for the exercise:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Authentication Error!</title>
</head>
<body>
<h1>Authentication Error!</h1>
<p>
Oops! You either keyed in the wrong username or password. 
</p>
<a href="javascript:history.back(1)">Try again ?</a>
</body>
</html>

There are no mandatory elements here. The link to the previous page is a good idea, to allow the user to return to the login screen.

7.2.7. Edit Tomcat's server.xml To Enable JDBC Realm

By default, the JDBC Realm option is not enabled. You will need to edit the server.xml file to enable it. Locate the following section,

     
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 debug="0" resourceName="UserDatabase"/>

And comment it out, like so,

      <!--
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 debug="0" resourceName="UserDatabase"/>
      -->

Now, locate the following lines in the same file

      <!-- Replace the above Realm with one of the following to get a Realm
           stored in a database and accessed via JDBC -->

You will need to uncomment one of the JDBC realm definitions below these lines, taking care to substitute the values for your own database configurations values.

For example, I am using the Firebird database, and my JDBC driver and database parameters are as follows:

Table 7-3. Firebird database and JDBC driver parameters

Parameter Name server.xml equivalent Parameter Value Remarks
Driver Name driverName org.firebirdsql.jdbc.FBDriver This value was provided by the JDBC driver documentation. This is typically the JDBC driver name you would use when programming a simple JDBC application.
JDBC Connection URL connectionURL jdbc:firebirdsql:localhost/3050:/opt/firebird/db/realmdb.fdb To find out the syntax and values of your connection URL, refer to your JDBC driver documentation
Database login name connectionName SYSDBA This is the username you use to login to the database containing the realm users' credentials.
Database login password connectionPassword password This is the login user's password
User Table userTable tcusers Table containing list of authorized users for the realm
Name Column of User Table userNameCol user_name The name of the column containing the list of users inside the User Table
User Password Column of User Table userCredCol user_pwd The name of the column containing the respective user's password in the User Table
Role Table userRoleTable tcroles Table containing the list of users and their respective roles
Role Name Column roleNameCol role_name The name of the column containing the user's assigned roles

With this information, we can form the following required stanza for the JDBC realm:

<Realm  className="org.apache.catalina.realm.JDBCRealm" debug="0"
             driverName="org.firebirdsql.jdbc.FBDriver"
          connectionURL="jdbc:firebirdsql:localhost/3050:/opt/firebird/db/realmdb.fdb"
         connectionName="sysdba" connectionPassword="password"
              userTable="tcusers" userNameCol="user_name" userCredCol="user_pwd"
          userRoleTable="tcroles" roleNameCol="role_name" />

7.2.8. Edit the Web Application's web.xml To Require Authentication

We need to configure the web application to require authentication as well. At this stage, we determine which resources to protect, who has access, and how we want to protect these resources. That is, we define the directories and/or the files to protect (html, jsp, image files or all files), which role has access and which type of authentication we want to use (Form-based, Basic, Custom or Digest).

For this exercise, this is what we want to protect:

  • Everything in the MyFirst application, that is, all HTML files, image files, JSP files, servlets, text files, everything!

  • Any and all access methods, that is, HTTP GET, PUT, POST, DELETE, will get the login prompt.

Who can access it:

  • Only users with the 'tomcat' role are allowed to access the MyFirst web application

How we want to protect the resources:

  • We will use Form-based authentication

  • The login page is login.html

  • The error page, if the user keys in the wrong username or password is autherr.html

We also need to define the roles that will have access. For this exercise, we only want users with the 'tomcat' role to have access.

To express these requirements, we key in the stanza below into the web application's web.xml file, after the <servlet-mapping> section and before the terminating </web-app> tag.

<security-constraint>
        <web-resource-collection>
                <web-resource-name>MyFirst</web-resource-name>
                <description> accessible by authenticated users of the tomcat role</description>
                <url-pattern>/*</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
                <http-method>PUT</http-method>
                <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
                <description>These roles are allowed access</description>
                <role-name>tomcat</role-name>
        </auth-constraint>
</security-constraint>

<login-config>
        <auth-method>FORM</auth-method>
        <realm-name>MyFirst Protected Area</realm-name>
        <form-login-config>
                <form-login-page>/login.html</form-login-page>
                <form-error-page>/autherr.html</form-error-page>
        </form-login-config>
</login-config>

<security-role>
        <description>Only 'tomcat' role is allowed to access this web application</description>
        <role-name>tomcat</role-name>
</security-role>

The order of the sections is important! If you get errors on Tomcat startup, it may be due to the ordering of your sections. For example, the <security-constraint> section can ONLY come after the <servlet-mapping> section. For more details on the proper order, read the Servlet Specifications document.

Now, we are finally ready to test our JDBC realm !

7.2.9. Start Tomcat and Test

Verify that the login.html and autherr.html files are in the MyFirst web application directory. Your Tomcat engine should be stopped at this time. Start Tomcat and open a browser. Try to access the HelloWorld servlet. You should get redirected to the login page (see below).

Key in the username and password of a user who does *not* have the tomcat role. You should get the error page.

Next, key in the username and password of a user who has the 'tomcat' role, and click the Login button. You should then see the familiar HelloWorld servlet page.