Useful information for configuring Tomcat DataSourceRealms is spread out over three documents. This often leads to people using a simple JDBCRealm for authentication and authorization. There are at least two challenges when using a simple JDBCRealm for this purpose.
The Tomcat DataSource realm addresses these issues by using a JNDI datasource. The realm can be configured with adequate pooling parameters to reduce synchronization issues, and a validationQuery to prevent database connection timeouts.
This document takes information from three Tomcat documents to describe some ways to configure DataSource Realms for authorization and authentication.
The following two environments are used while writing this document.
Component |
Version |
OS |
Fedora 13 32 bit |
JDK/JRE |
1.6.0_20 |
Tomcat |
6.0.26 |
Apache Derby |
10.5.3.0 |
IDE |
NetBeans 6.8 |
Hibernate |
3.2.5 ga |
JSF |
1.2 |
|
|
OS |
Windows/XP Professional SP 4 32 bit |
JDK/JRE |
1.6.0_20 |
Tomcat |
6.0.26 |
Apache Derby |
5.1.31 |
IDE |
NetBeans 6.8 |
Hibernate |
10.5.3.0 |
JSF |
1.2 |
In order to test these configurations, a simple JSF / Hibernate / CRUD application based on the the following NetBeans JSF Tutorial was used. The entire application was wrapped up in a BASIC authentication scheme using the following web.xml portion.
<security-constraint> <display-name>Entire application</display-name> <web-resource-collection> <web-resource-name>Everything</web-resource-name> <description>coarse grained approach</description> <url-pattern>/faces/*</url-pattern> </web-resource-collection> <auth-constraint> <description/> <role-name>user</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>Hibernate Application</realm-name> </login-config> <security-role> <description>generic user of application</description> <role-name>user</role-name> </security-role> |
Finally, a separate authorization and authentication database was created in Apache Derby. This database consists of three tables and one view.
The DataSource realm actually consists of two components.
The first component is a JNDI JDBC data source resource. Documentation for setting this up can be found here:
Using a JNDI JDBC data source resource requires the JDBC driver to be visible to Tomcat. Thus, the JDBC driver needs to be placed in $CATALINA_BASE/lib (for Tomcat 6). Once this is done, do not put this driver in the application's WEB-INF/lib directory.
The second component is the actual Realm. Documentation for setting this up can be found in the following locations:
In particular, pay attention to the table and column mappngs required for the DataSource realm. Combining this information leads to a working DataSource realm for authentication and authorization.
Three configuration scenarios are presented below.
This is appropriate when each web application might use different authentication and authorization databases. This is also the simplest configuration to manage, since all configuration elements are in one location. Finally, this configuration will produce the most portable (between Tomcat installations) war file.
A Resource element is created in META-INF/context.xml to describe the database connection and provide a JNDI name. This is the same type of Resource description that is used for application - level JNDI data source. A sample fragment is shown below.
<Resource name="jdbc/auth" description="Sample authentication" type="javax.sql.DataSource" auth="Container" driverClassName="org.apache.derby.jdbc.ClientDriver" maxActive="10" maxIdle="3" maxWait="10000" password="PASSWORD" url="jdbc:derby://localhost:1527/authorize" validationQuery="values(1)" username="USER"/> |
This Resource element describes a connection to the authorization database mentioned in the Environments section above. Some items to note about the configuration are:
The Realm element references the Resource element given above. The section of META-INF/context.xml describing the Realm element is given below.
<Realm className="org.apache.catalina.realm.DataSourceRealm" userTable="APP.USERS" userNameCol="USERNAME" userCredCol="PASSWORD" userRoleTable="APP.AUTH" roleNameCol="ROLENAME" localDataSource="true" dataSourceName="jdbc/auth"/> |
The Realm element above describes a table and column mapping between the database described in the Environments section and the required elements for authorization and authentication. Some items to note about the above configuration are listed below:
The completed META-INF/context.xml file is shown below.
<?xml version="1.0" encoding="UTF-8"?> <Context antiJARLocking="true" path="/HibernateApp"> <Resource name="jdbc/auth" description="Sample authentication" type="javax.sql.DataSource" auth="Container" driverClassName="org.apache.derby.jdbc.ClientDriver" maxActive="10" maxIdle="3" maxWait="10000" password="PASSWORD" url="jdbc:derby://localhost:1527/authorize" validationQuery="values(1)" username="USER"/> <Realm className="org.apache.catalina.realm.DataSourceRealm" userTable="APP.USERS" userNameCol="USERNAME" userCredCol="PASSWORD" userRoleTable="APP.AUTH" roleNameCol="ROLENAME" localDataSource="true" dataSourceName="jdbc/auth"/> </Context> |
This configuration can be appropriate when multiple applications need to use the same authentication and authorization database. The JNDI resource is described in the GlobalNamingResources element of $CATALINA_BASE/conf/server.xml. Each application that requires authentication and authorization via this resource should a Realm definition in META-INF/context.xml referencing the global name.
The Resource element used in the GlobalNamingResources is the same one that is described above. The only difference is its placement. Below is the default GlobalNamingResources element (without comments) as shipped with Tomcat 6.
<GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> |
Adding the authentication and authorization resource to the above default implementation creates the following GlobalNamingResources element in $CATALINA_BASE/conf/server.xml.
<GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> <Resource name="jdbc/auth" description="Sample authentication" type="javax.sql.DataSource" auth="Container" driverClassName="org.apache.derby.jdbc.ClientDriver" maxActive="10" maxIdle="3" maxWait="10000" password="PASSWORD" url="jdbc:derby://localhost:1527/authorize" validationQuery="values(1)" username="USER"/> </GlobalNamingResources> |
This entry makes the authentication and authorization database available to all applications by referencing the JNDI name jdbc/auth.
NOTE: In order to make the new Resource available, Tomcat will have to be restarted once the $CATALINA_BASE/conf/server.xml file has been modified.
Finally, in order for the web application to use this authentication and authorization resource, a Realm element needs to be added to META-INF/context.xml. An example is shown below.
<Realm className="org.apache.catalina.realm.DataSourceRealm" userTable="APP.USERS" userNameCol="USERNAME" userCredCol="PASSWORD" userRoleTable="APP.AUTH" roleNameCol="ROLENAME" dataSourceName="jdbc/auth"/> |
Items to note are listed below.
The completed META-INF/context.xml file is shown below.
<?xml version="1.0" encoding="UTF-8"?> <Context antiJARLocking="true" path="/HibernateApp"> <Realm className="org.apache.catalina.realm.DataSourceRealm" userTable="APP.USERS" userNameCol="USERNAME" userCredCol="PASSWORD" userRoleTable="APP.AUTH" roleNameCol="ROLENAME" dataSourceName="jdbc/auth"/> </Context> |
Sometimes every sub-element under a particular element requires the same set of authentication and authorization resources. Rather than duplicating the configuration for multiple resources, it may make sense to place both the Resource and Realm in $CATALINA_BASE/conf/server.xml. Possible scenarios are listed below.
Each web application that wishes to make use of the $CATALINA_BASE/conf/server.xml - defined Realm must still obviously have security constraints configured in WEB-INF/web.xml.* *
Tomcat resolves multiple Realm definitions by using the most specific one for a given element. Examples are given below.
One way to manage multiple Realms in $CATALINA_BASE/conf/server.xml is to use a CombinedRealm. The CombinedRealm provides a container for other Realms (sub-Realms). These Realms are tried in the order configured, until an authentication match is is made or all sub-Realms are tried.
Care should be taken that authentication and authorization information does not unintentionally overlap. Some of the consequences are discussed below.
If a username/password for App2 exists in the first sub-Realm, then authorization depends on whether or not the appropriate username/role also exists in the first sub-Realm.
If a role for App2 exists in the first sub-Realm, then a user authenticating in that sub-Realm could gain inappropriate access to App2 depending on the username/role mapping.
There are also benefits to this approach. One sub-Realm could be used as an "administrator" Realm, while other sub-Realms could provide authentication and authorization for specific applications.
The following steps can be used to configure a DataSource Realm in $CATALINA_BASE/conf/server.xml using a CombinedRealm.
Add the required Resource element to the GlobalNamingResources element in $CATALINA_BASE/conf/server.xml. Below is the default GlobalNamingResources element (without comments) as shipped with Tomcat 6.
<GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> |
Adding the authentication and authorization resource to the above default implementation creates the following GlobalNamingResources element in $CATALINA_BASE/conf/server.xml.
<GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> <Resource name="jdbc/auth" description="Sample authentication" type="javax.sql.DataSource" auth="Container" driverClassName="org.apache.derby.jdbc.ClientDriver" maxActive="10" maxIdle="3" maxWait="10000" password="PASSWORD" url="jdbc:derby://localhost:1527/authorize" validationQuery="values(1)" username="USER"/> </GlobalNamingResources> |
This entry makes the authentication and authorization database available to all applications by referencing the JNDI name jdbc/auth.
NOTE: In order to make the new Resource available, Tomcat will have to be restarted once the $CATALINA_BASE/conf/server.xml file has been modified.
In order to avoid overriding the existing Engine-level Realm element in Tomcat's default $CATALINA_BASE/conf/server.xml, a CombinedRealm container will be used.
First, here is the default Realm as shipped with Tomcat 6.
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> |
Surround this Realm element with another Realm element defining the CombinedRealm. Within that element place both the default Tomcat UserDatabaseRealm and the DataSourceRealm. The resulting section of $CATALINA_BASE/conf/server.xml will look like the following.
<Realm className="org.apache.catalina.realm.CombinedRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <Realm className="org.apache.catalina.realm.DataSourceRealm" userTable="APP.USERS" userNameCol="USERNAME" userCredCol="PASSWORD" userRoleTable="APP.AUTH" roleNameCol="ROLENAME" dataSourceName="jdbc/auth"/> </Realm> |
NOTE: With both Realm and Resource information in $CATALINA_BASE/conf/server.xml, no Realm or Resource elements pertaining to authorization and authentication should appear in META-INF/context.xml. An application may require other Resource elements, but any Realm element in META-INF/context.xml will override that provided in $CATALINA_BASE/conf/server.xml.
The following outline summarizes the three approaches discussed above.