Container Adapter Authentication
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
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.
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").
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.
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
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.