Spring in Practice

Willie Wheeler's Spring blog

Acegi Overview (Now Spring Security)

| Comments

I wrote this back when Spring Security was called Acegi. Now it’s out of date, but I’m leaving it in the archive.

Acegi has been around for a while, but I just recently tried it out and am impressed with it so far. Upon first glance Acegi is slightly intimidating because there are lots of classes involved, and it may be hard to keep them straight. Fortunately Acegi is actually straightforward, despite appearances, to anybody who already understands servlet filters and the basics of Spring bean configuration.

The overarching idea is that Acegi provides a suite of security services (mostly if not entirely around authentication and authorization) based on servlet filters. So for example there are filters for accepting user logins, enforcing access controls, catching security-related exceptions,handling logouts, handling “remember me” functionality for logins, switching from HTTP to HTTPS, etc. So using Acegi mostly involves deciding which of the filters you want (e.g., maybe you want “remember me”, maybe you don’t) and then configuring those into the app.

Acegi involves dependency injection into servlet filters (say what?)

Acegi does get a little interesting though because the various filters rely upon Spring’s dependency injection, but the standard way of configuring servlet filters into web.xml does not support Spring dependency injection. (The web.xml schema doesn’t have anything for dependency injection, nor should it.) Acegi uses a pretty neat trick to give us injectable servlet filters. What it does is it provides a Filter implementation called FilterToBeanProxy that you configure in like this:

<filter>
    <filter-name>security</filter-name>
    <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
    <init-param>
        <param-name>targetClass</param-name>
        <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
    </init-param>
</filter>
 
<filter-mapping>
    <filter-name>security</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Pretty standard stuff. The idea behind FilterToBeanProxy is that it knows about your [appname]-security.xml bean configuration file, and so it can load in any beans that you define there. In web.xml we tell FilterToBeanProxy to load in a FilterChainProxy defined in [appname]-security.xml. FilterChainProxy,as its name suggests, is a FilterChain implementation. In turn, inside [appname]-security.xml we configure the various Filter beans mentioned above into the FilterChainProxy. Here’s how it looks:

<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
    <property name="filterInvocationDefinitionSource">
        <!-- IMPORTANT: DON'T LINEBREAK THE FILTER LIST, OR ELSE BEAN LOOKUP BREAKS! -->
        <!-- I'M JUST DOING IT HERE FOR DISPLAY PURPOSES -->
        <value>
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
            /**=httpSessionContextIntegrationFilter,logoutFilter,
authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
        </value>
    </property>
</bean>

The list of filter names is just a list of beans that you also configure in the same [appname]-security.xml config file. Here’s an example:

<bean id="httpSessionContextIntegrationFilter"
    class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>

Voilà, injectable servlet filters! Very cool.

Be careful of the following gotchas

There are some points/gotchas worth mentioning:

  1. The servlet filters must be added in a specific order. If you get the order wrong, it won’t work, since the servlet filters make assumptions about what kind of processing has already taken place. Check the Acegi docs for details.
  2. If you’re using other servlet filters (such as Sitemesh), the Acegi filters need to come before the others.
  3. When configuring FilterChainProxy, you will need to map URL patterns to the list of filters that you want to service requests matching those patterns. For example, you might map /** (Ant syntax) to a list of four filters. The list is comma-delimited. It is important that you not include line breaks after the commas. I’m guessing you can’t include whitespace at all, but for sure you can’t include line breaks. Otherwise you will get Bean not found: '' exceptions, without any explanation as to what the actual problem is. I’m surprised they don’t allow the line breaks since I figure many people will naturally want to break up a long list of filters with long names,but my surprise notwithstanding, the line breaks aren’t allowed.

Here’s a JavaWorld article on getting started with Acegi. Also, Spring in Action has some good instructions.

Comments