Upgrading to Spring Security 2.0

This evening I spent a few hours and upgraded AppFuse to use Acegi Spring Security 2.0. The upgrade was fairly straightforward:

  • %s/org.acegisecurity/org.springframework.security/g
  • Upgraded dependencies (exclusions are necessary if you're using Spring 2.5.x and don't want 2.0.x dependencies pulled in):
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core-tiger</artifactId>
        <version>${spring.security.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-support</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    ...
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>${spring.security.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
  • Changed taglib prefix from "authz" to "security" and change the associated taglib declaration to:
    <%@ taglib uri="http://www.springframework.org/security/tags" 
        prefix="security" %>
    
  • In web.xml, I changed <filter-class> to org.springframework.web.filter.DelegatingFilterProxy. Since I didn't name my filter springSecurityFilterChain, I also had to add the following <init-param>:
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>springSecurityFilterChain</param-value>
        </init-param>
    
  • Lastly, I modified security.xml to use the new syntax. AppFuse's security.xml went from 175 lines to 33 with the new security namespace configuration!

It's hard to believe I first looked at Acegi almost 4 years ago. At that time, I said it contained too much XML for my needs. Ben's reaction:

Seriously, the "whole lotta XML" gives you exponentially more power and flexibility than a method such as this could ever hope to provide you.

It's nice to see that Spring Security 2.0 gives you exponentially more power and flexibility without all the XML. Thanks guys!

P.S. You can also view the full changelog for this upgrade.

Update: If you're using <authz:authentication property="fullName"/> in your JSPs, you'll need to change it to <security:authentication property="principal.fullName"/>.

Posted in Java at Apr 17 2008, 02:45:47 AM MDT 19 Comments
Comments:

great stuff, even if unfortunately it's still not that simple to configure different kind of autenthication based on urls... for example in appfuse I'm still trying to figure out how to use basic-auth for web services under /service and form login for the rest of the website

Posted by magomarcelo on April 17, 2008 at 11:37 AM MDT #

Magomarcelo, BASIC authentication is automatically supported out of the box. So if you present basic authentication headers, the SecurityContextHolder will automatically populate. So just add an <intercept-url> containing the appropriate configuration attributes for your /services/** URL pattern. Or, use one of the many supported method security approaches (<protect-pointcut>, @Secured, JSR 250 security annotations, or <intercept-methods>). You can read more about the new syntax at http://static.springframework.org/spring-security/site/reference/html/namespace-config.html. Or feel free to post a question at http://forum.springframework.org.

Posted by Ben Alex on April 17, 2008 at 03:12 PM MDT #

Thanks Ben, I've added a protected /services/* intercept-url and a pass-through /services/*?wsdl with filters=none in Matt's configuration but from SoapUI setting the basic-auth I always get a 302 with a redirect to the form login page, but this is definetely an issue for the forums. what instead I would really like and I still don't see in 2.0 is an easy way to say that some webservices URLs are http-basic auth only, with a proper 401 prompt and no login form, while the rest of my webapp, being a real UI for humans, has form login

Posted by magomarcelo on April 17, 2008 at 06:52 PM MDT #

I also needed to exclude the following:

<exclusion>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
</exclusion>
<exclusion>
    <groupId>org.springframework</groupId>
    <artifactId>spring-dao</artifactId>
</exclusion>
<exclusion>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</exclusion>

Posted by Graham Parker on April 19, 2008 at 07:40 AM MDT #

First of all many thanks for your great work so far... I am migrating a project from Perl to Java and had to incorporate into all this Java web frameworks... AppFuse and the tutorials helped me almost solely to pick the right framework for my needs and got me started pretty fast...

Yesterday I tried to merge your update to Spring Security 2.0 into my 'full-source' project... Everything went well with the help of a project-wide find & replace and the change log except one thing:

In security.xml (line 23) you're setting the salt-source to the username:

  <salt-source user-property="username"/>

This does not fit to the call of the encodePassword() method in UserManagerImpl.java (line 94) where you're using no salt (null in the second parameter)... Thus a user can register with a password but not login with it...

If you like to change the salt, the pre-defined passwords in "default-data.xml" also have to be updated...

One fix is to comment out the line above...

Thanks again and best greetings...
:-) Martin

Posted by Martin Pietsch on April 22, 2008 at 03:32 AM MDT #

@magomarcelo

If you read the manual or reference guide, there are CLEAR instructions on how to configure your app for webservices with basic auth as below,

<bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
  <sec:filter-chain-map path-type="ant">
     <sec:filter-chain pattern="/webServices/**" filters="httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor"/>
     <sec:filter-chain pattern="/**" filters="httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor"/>
  </sec:filter-chain-map>
</bean>

Posted by Sarat Pediredla on April 22, 2008 at 06:13 AM MDT #

Less a comment to Matt -- more a hint to other users in my situation (I installed AppFuse 2.0.1 'full-source' and wanted to update to Spring Security 2.0)

One have to ensure, that

<spring.version>2.5.3</spring.version>

is used in pom.xml. Using 2.5 for example will result in an ava.lang.NoSuchMethodError: org.springframework.aop.config.AopNamespaceUtils.registerAutoProxyCreatorIfNecessary exception, when there are <protect-pointcut expression="execution( ... )" access="ROLE_ADMIN"/> statements in the security.xml.

But this won't fix everything... There are some more dependencies, that have changed...

The best is to merge all changes of the pom.xml between your Appfuse release and the HEAD into your pom.xml. In my case merging all changes from rev. 3060 (release 2.0.1) to 3089 (HEAD) solved all my problems.

:) Martin

Posted by Martin Pietsch on April 22, 2008 at 07:18 AM MDT #

Matt: any idea when the next version of AppFuse w/ this Spring Security upgrade will be released? Tapestry 5 also looks like it might be close to release as well :-)

Posted by ken on April 22, 2008 at 12:36 PM MDT #

Version 2.0.2 will have Spring Security 2.x. I hope to release it before JavaOne, but it's been tough for me to find time to work on open source lately.

Posted by Matt Raible on April 22, 2008 at 06:11 PM MDT #

I've finally found a configuration that is working for my requirements and testing with SoapUI: I had to redefine the exceptionTranslationFilter to have a proper Basic-Auth prompt and activate the request-challenge-response behaviour.

Here's a snippet from security.xml:

<security:http auto-config="true" lowercase-comparisons="false" realm="My Basic-Auth Realm">
    <security:intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
    <security:intercept-url pattern="/services/*?wsdl" filters="none"/>
    <security:intercept-url pattern="/services/*Service" access="ROLE_ADMIN,ROLE_USER"/>
    <security:intercept-url pattern="/passwordHint.html*" 
        access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
    <security:intercept-url pattern="/signup.html*" 
        access="ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER"/>
    <security:intercept-url pattern="/**/*.html*" access="ROLE_ADMIN,ROLE_USER"/>
    <security:http-basic/>
    <security:form-login login-page="/login.jsp" 
        authentication-failure-url="/login.jsp?error=true" 
        login-processing-url="/j_security_check"/>
    <security:remember-me user-service-ref="userDao" key="23_*!cdU='612./e;NrI"/>
</security:http>

<bean id="securityFilter" class="org.springframework.security.util.FilterChainProxy">
    <security:filter-chain-map path-type="ant">
        <security:filter-chain pattern="/services/**"
            filters="httpSessionContextIntegrationFilterWithASCFalse,
                   _basicAuthenticationFilter,basicExceptionTranslationFilter,
                   _filterSecurityInterceptor"/>
        <security:filter-chain pattern="/**"              
            filters="_httpSessionContextIntegrationFilter,_formLoginFilter,
                     _securityContextHolderAwareRequestFilter,_rememberMeFilter,
                     _anonymousProcessingFilter,_exceptionTranslationFilter,
                     _filterSecurityInterceptor"/>
    </security:filter-chain-map>
</bean>

<bean id="basicExceptionTranslationFilter" 
    class="org.springframework.security.ui.ExceptionTranslationFilter"
    p:authenticationEntryPoint-ref="_basicAuthenticationEntryPoint">
    <property name="accessDeniedHandler">
        <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl"/>
    </property>
</bean>

<bean id="httpSessionContextIntegrationFilterWithASCFalse"
    class="org.springframework.security.context.HttpSessionContextIntegrationFilter" 
    p:allowSessionCreation="false"/>

and another snippet from web.xml:

<filter>
    <filter-name>securityFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <!-- <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>springSecurityFilterChain</param-value>
    </init-param> -->
</filter>

Posted by magomarcelo on April 24, 2008 at 07:36 AM MDT #

I am facing the same problem you posted. Could you please let me know the fix?

Posted by John on April 24, 2008 at 06:56 PM MDT #

John - I don't know the fix. I got around it by setting the passwordEncoder as its own bean and getting it from the ApplicationContext.

Posted by Matt Raible on April 24, 2008 at 09:53 PM MDT #

Magomarcelo's solution works fine, but it took me a while to understand exactly how. The <http> security tag sets up the FilterChainProxy that your requests go through, and is a much shorter way to do it than the previous Acegi security, where each filter had to be initialised separately in the security.xml file.

But if you want to put your requests through different filter chains based on URL patterns, as MM and I did, then you need to set these up manually, and not use the <http> one.

So this solution uses <http> to take the hard work out of initialising all the filters (the bean names are the filter class names lowercased, and with an underscore prefix, e.g. _filterSecurityInterceptor), but then sets up the FilterChainProxy we actually use, referencing these filters set up with the <http> tag, with a couple of extra filters initialised at the end for custom handing of the basic auth requests. The filters are used in the order they are named.

I used a slightly different format for the extra filter declarations, wasn't sure what was bound to the p: namespace in his example:

<bean id="basicExceptionTranslationFilter"
    class="org.springframework.security.ui.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint" ref="_basicAuthenticationEntryPoint"/>
    <property name="accessDeniedHandler">
        <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl"/>
    </property>
</bean>

<bean id="httpSessionContextIntegrationFilterWithASCFalse"
    class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
    <property name="allowSessionCreation" value="false"/>
</bean>

Posted by Benito on June 05, 2008 at 07:08 AM MDT #

Thanks for this information, Matt! It is invaluable and currently you have a monopoly on it.

Tell me something. Why is it that the only place to find information on how to upgrade to Spring Security on a website that is not related to Spring Security?

[Explicative Deleted] ridiculous.

The documentation for Acegi has always been terrible. This is just one more example. On their website, the very first link in their menu is for building with Maven. But search their site for anything about how to include Acegi into your existing Maven project. Search results: Nada.

Oh, the irony!

Posted by Mike on June 22, 2008 at 10:39 AM MDT #

Matt,

Both links given in the line below are not working and am unable to access the security.xml file in fisheye.

Lastly, I modified security.xml to use the new syntax. AppFuse's security.xml went from 175 lines to 33 with the new security namespace configuration!

(FishEye was unable to process your request. java.lang.IllegalStateException

Timer already cancelled.)

thanks,
aaron

Posted by aaron on July 28, 2008 at 01:46 PM MDT #

Sorry about that aaron - should be fixed now.

Posted by Matt Raible on July 29, 2008 at 09:45 PM MDT #

Thanks a lot for this article... I've always just kind of winged it when it comes to citations.

Posted by Andre on October 16, 2008 at 01:13 PM MDT #

and this is one of the main reasons why I've moved to Rails ... I wanna work on my problem not spend my time merging xml files....

Posted by Eric on January 09, 2009 at 12:08 AM MST #

Eric, I would retort with: that is why I moved to Grails. You would not have lost credibility with that remark.

Posted by Mike on January 09, 2009 at 08:25 AM MST #

Post a Comment:
  • HTML Syntax: Allowed