Container Adapter Authentication

21.1. Overview

Very early versions of Spring Security exclusively used Container Adapters for interfacing authentication with end users. Whilst this worked well, it required considerable time to support multiple container versions and the configuration itself was relatively time-consuming for developers. For this reason the HTTP Form Authentication and HTTP Basic Authentication approaches were developed, and are today recommended for almost all applications.

Container Adapters enable Spring Security to integrate directly with the containers used to host end user applications. This integration means that applications can continue to leverage the authentication and authorization capabilities built into containers (such as isUserInRole() and form-based or basic authentication), whilst benefiting from the enhanced security interception capabilities provided by Spring Security (it should be noted that Spring Security also offers ContextHolderAwareRequestWrapper to deliver isUserInRole() and similar Servlet Specification compatibility methods).

The integration between a container and Spring Security is achieved through an adapter. The adapter provides a container-compatible user authentication provider, and needs to return a container-compatible user object.

The adapter is instantiated by the container and is defined in a container-specific configuration file. The adapter then loads a Spring application context which defines the normal authentication manager settings, such as the authentication providers that can be used to authenticate the request. The application context is usually named acegisecurity.xml and is placed in a container-specific location.

Spring Security currently supports Jetty, Catalina (Tomcat), JBoss and Resin. Additional container adapters can easily be written

21.2. Adapter Authentication Provider

As is always the case, the container adapter generated Authentication object still needs to be authenticated by an AuthenticationManager when requested to do so by the AbstractSecurityInterceptor. The AuthenticationManager needs to be certain the adapter-provided Authentication object is valid and was actually authenticated by a trusted adapter.

Adapters create Authentication objects which are immutable and implement the AuthByAdapter interface. These objects store the hash of a key that is defined by the adapter. This allows the Authentication object to be validated by the AuthByAdapterProvider. This authentication provider is defined as follows:

<bean id="authByAdapterProvider"
        class="org.springframework.security.adapters.AuthByAdapterProvider">
<property name="key"><value>my_password</value></property>
</bean>       

The key must match the key that is defined in the container-specific configuration file that starts the adapter. The AuthByAdapterProvider automatically accepts as valid any AuthByAdapter implementation that returns the expected hash of the key.

To reiterate, this means the adapter will perform the initial authentication using providers such as DaoAuthenticationProvider, returning an AuthByAdapter instance that contains a hash code of the key. Later, when an application calls a security interceptor managed resource, the AuthByAdapter instance in the SecurityContext in the SecurityContextHolder will be tested by the application's AuthByAdapterProvider. There is no requirement for additional authentication providers such as DaoAuthenticationProvider within the application-specific application context, as the only type of Authentication instance that will be presented by the application is from the container adapter.

Classloader issues are frequent with containers and the use of container adapters illustrates this further. Each container requires a very specific configuration. The installation instructions are provided below. Once installed, please take the time to try the sample application to ensure your container adapter is properly configured.

When using container adapters with the DaoAuthenticationProvider, ensure you set its forcePrincipalAsString property to true.

21.3. Jetty

The following was tested with Jetty 4.2.18.

$JETTY_HOME refers to the root of your Jetty installation.

Edit your $JETTY_HOME/etc/jetty.xml file so the <Configure class> section has a new addRealm call:

<Call name="addRealm">
<Arg>
  <New class="org.springframework.security.adapters.jetty.JettySpringSecurityUserRealm">
    <Arg>Spring Powered Realm</Arg>
    <Arg>my_password</Arg>
    <Arg>etc/acegisecurity.xml</Arg>
  </New>
</Arg>
</Call>

    

Copy acegisecurity.xml into $JETTY_HOME/etc.

Copy the following files into $JETTY_HOME/ext:

  • aopalliance.jar

  • commons-logging.jar

  • spring.jar

  • acegi-security-jetty-XX.jar

  • commons-codec.jar

  • burlap.jar

  • hessian.jar

None of the above JAR files (or acegi-security-XX.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does matter with Jetty. The web.xml must express the same <realm-name> as your jetty.xml (in the example above, "Spring Powered Realm").

21.4. JBoss

The following was tested with JBoss 3.2.6.

$JBOSS_HOME refers to the root of your JBoss installation.

There are two different ways of making spring context available to the Jboss integration classes.

The first approach is by editing your $JBOSS_HOME/server/your_config/conf/login-config.xml file so that it contains a new entry under the <Policy> section:

<application-policy name = "SpringPoweredRealm">
<authentication>
  <login-module code = "org.springframework.security.adapters.jboss.JbossSpringSecurityLoginModule"
        flag = "required">
    <module-option name = "appContextLocation">acegisecurity.xml</module-option>
    <module-option name = "key">my_password</module-option>
 </login-module>
</authentication>
</application-policy>

    

Copy acegisecurity.xml into $JBOSS_HOME/server/your_config/conf.

In this configuration acegisecurity.xml contains the spring context definition including all the authentication manager beans. You have to bear in mind though, that SecurityContext is created and destroyed on each login request, so the login operation might become costly. Alternatively, the second approach is to use Spring singleton capabilities through org.springframework.beans.factory.access.SingletonBeanFactoryLocator. The required configuration for this approach is:

<application-policy name = "SpringPoweredRealm">
<authentication>
  <login-module code = "org.springframework.security.adapters.jboss.JbossSpringSecurityLoginModule"
        flag = "required">
    <module-option name = "singletonId">springRealm</module-option>
    <module-option name = "key">my_password</module-option>
    <module-option name = "authenticationManager">authenticationManager</module-option>
 </login-module>
</authentication>
</application-policy>

    

In the above code fragment, authenticationManager is a helper property that defines the expected name of the AuthenticationManager in case you have several defined in the IoC container. The singletonId property references a bean defined in a beanRefFactory.xml file. This file needs to be available from anywhere on the JBoss classpath, including $JBOSS_HOME/server/your_config/conf. The beanRefFactory.xml contains the following declaration:

<beans>
<bean id="springRealm" singleton="true" lazy-init="true" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
  <list>
    <value>acegisecurity.xml</value>
  </list>
</constructor-arg>
</bean>
</beans>

    

Finally, irrespective of the configuration approach you need to copy the following files into $JBOSS_HOME/server/your_config/lib:

  • aopalliance.jar

  • spring.jar

  • acegi-security-jboss-XX.jar

  • commons-codec.jar

  • burlap.jar

  • hessian.jar

None of the above JAR files (or acegi-security-XX.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with JBoss. However, your web application's WEB-INF/jboss-web.xml must express the same <security-domain> as your login-config.xml. For example, to match the above example, your jboss-web.xml would look like this:

<jboss-web>
<security-domain>java:/jaas/SpringPoweredRealm</security-domain>
</jboss-web>

JBoss is a widely-used container adapter (mostly due to the need to support legacy EJBs), so please let us know if you have any difficulties.

21.5. Resin

The following was tested with Resin 3.0.6.

$RESIN_HOME refers to the root of your Resin installation.

Resin provides several ways to support the container adapter. In the instructions below we have elected to maximise consistency with other container adapter configurations. This will allow Resin users to simply deploy the sample application and confirm correct configuration. Developers comfortable with Resin are naturally able to use its capabilities to package the JARs with the web application itself, and/or support single sign-on.

Copy the following files into $RESIN_HOME/lib:

  • aopalliance.jar

  • commons-logging.jar

  • spring.jar

  • acegi-security-resin-XX.jar

  • commons-codec.jar

  • burlap.jar

  • hessian.jar

Unlike the container-wide acegisecurity.xml files used by other container adapters, each Resin web application will contain its own WEB-INF/resin-acegisecurity.xml file. Each web application will also contain a resin-web.xml file which Resin uses to start the container adapter:

<web-app>
<authenticator>
<type>org.springframework.security.adapters.resin.ResinAcegiAuthenticator</type>
<init>
  <app-context-location>WEB-INF/resin-acegisecurity.xml</app-context-location>
  <key>my_password</key>
</init>
</authenticator>
</web-app>

    

With the basic configuration provided above, none of the JAR files listed (or acegi-security-XX.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with Resin, as the relevant authentication class is indicated by the <authenticator> setting

21.6. Tomcat

The following was tested with Jakarta Tomcat 4.1.30 and 5.0.19.

$CATALINA_HOME refers to the root of your Catalina (Tomcat) installation.

Edit your $CATALINA_HOME/conf/server.xml file so the <Engine> section contains only one active <Realm> entry. An example realm entry:

      <Realm
        className="org.springframework.security.adapters.catalina.CatalinaSpringSecurityUserRealm"
        appContextLocation="conf/acegisecurity.xml"
         key="my_password" />

Be sure to remove any other <Realm> entry from your <Engine> section.

Copy acegisecurity.xml into $CATALINA_HOME/conf.

Copy spring-security-catalina-XX.jar into $CATALINA_HOME/server/lib.

Copy the following files into $CATALINA_HOME/common/lib:

  • aopalliance.jar

  • spring.jar

  • commons-codec.jar

  • burlap.jar

  • hessian.jar

None of the above JAR files (or spring-security-XX.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with Catalina.

We have received reports of problems using this Container Adapter with Mac OS X. A work-around is to use a script such as follows:

#!/bin/sh
export CATALINA_HOME="/Library/Tomcat"
export JAVA_HOME="/Library/Java/Home"
cd /
$CATALINA_HOME/bin/startup.sh

Finally, restart Tomcat.