Method-Level Security in Spring Security 2.0 - No Fluff Just Stuff

Method-Level Security in Spring Security 2.0

Posted by: Craig Walls on April 15, 2008

Spring Security 2.0 was released today. To mark the occasion, I thought I'd write a little blog entry to show how you can use Spring Security to restrict method invocations to properly authorized users.

Let's say that you have a class with a method that must be secured...something like this:

package com.habuma.expectations.springsecurity.intercept;
public class SecuredObject {
   public String getSecuredData() {
      return "Top-Secret Data";
   }
}

In this simple example, we only want users who are granted "secret agent" authorization to be able to invoke the getSecuredData() method. In Spring Security 2.0, there are three ways to secure this method, starting with adding a method interceptor in the Spring XML:

<?xml version="1.0" encoding="UTF-8"?>
	
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:sec="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/security
               http://www.springframework.org/schema/security/spring-security-2.0.xsd">


   <bean id="securedObject" 
       class="com.habuma.expectations.springsecurity.intercept.SecuredObject">
     <sec:intercept-methods>
       <sec:protect access="ROLE_SECRET_AGENT" method="getSecuredData"/>
     </sec:intercept-methods>
   </bean>
...
</beans>

The <sec:intercept-methods> along with <sec:protect> tells Spring Security to intercept calls to getSecuredData() and only allow the method to be invoked if the Authentication object in the security context has "ROLE_SECRET_AGENT" authority. If there is no Authentication object in the security context (that is, the user hasn't been authenticated), then an AutheneticationCredentialsNotFoundException will be thrown. If the user has been authenticated, but does not posses the required authority, then an AccessDeniedException will be thrown.

Tweaking the XML as shown is pretty easy, but annotations make it even easier to secure methods. Let's change the XML to look like this:

<?xml version="1.0" encoding="UTF-8"?>
	
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:sec="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/security
               http://www.springframework.org/schema/security/spring-security-2.0.xsd">


   <bean id="securedObject" 
       class="com.habuma.expectations.springsecurity.intercept.SecuredObject" />

   <sec:global-method-security secured-annotations="enabled" />
...
</beans>

The <sec:global-method-security> element is used to specify security settings that apply across an entire application context. In this case, I've enabled annotation-based security by setting secured-annotations to "enabled". With this in place, I can now annotate the getSecuredData() method as follows:

package com.habuma.expectations.springsecurity.intercept;
import org.springframework.security.annotation.Secured;

public class SecuredObject {
   @Secured( {"ROLE_SECRET_AGENT"} )
   public String getSecuredData() {
      return "Top-Secret Data";
   }
}

What's great about the annotation-based approach is that I can now restrict access to any method in my application by simply annotating them with @Secured.

As nice as @Secured is, some of you may be wondering if a standards-compliant annotation might be a better choice. No problem...Spring Security can also support JSR-250's security annotations with the following adjustment to the XML configuration:

<?xml version="1.0" encoding="UTF-8"?>
	
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:sec="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/security
               http://www.springframework.org/schema/security/spring-security-2.0.xsd">


   <bean id="securedObject" 
       class="com.habuma.expectations.springsecurity.intercept.SecuredObject" />

   <sec:global-method-security jsr250-annotations="enabled" />
...
</beans>

Now the SecuredObject's getSecuredData() can be annotated with @RolesAllowed annotation from JSR-250:

package com.habuma.expectations.springsecurity.intercept;
import javax.annotation.security.RolesAllowed;

public class SecuredObject {
   @RolesAllowed( {"ROLE_SECRET_AGENT"} )
   public String getSecuredData() {
      return "Top-Secret Data";
   }
}

This barely scratches the surface of the goodness offered by Spring Security 2.0. If you want to hear more about it, then make plans to attend one of the No-Fluff/Just-Stuff events coming up in Seattle (this coming weekend), Oklahoma City, and Dallas where I'll be presenting Spring Security 2.0 (among other topics).

Note that the above examples all require some additional Spring Security configuration, specifically an authentication provider. To keep things simple, I've used the following authentication provider configuration in my Spring XML:

<sec:authentication-provider>
  <sec:user-service>
    <sec:user password="password" name="cwalls" authorities="ROLE_SECRET_AGENT"/>
    <sec:user password="password" name="intruder" authorities="ROLE_ANONYMOUS"/>
  </sec:user-service>
</sec:authentication-provider>

For a real application, you'll no doubt want to choose a different authentication provider, such as <jdbc-authentication-provider> or <ldap-authentication-provider>

For the adventurous reader who wants a quick way to try this stuff out, you can check out my learning-tests from my Subversion repository at svn://svn.geekisp.com/habuma/trunk/expectations/SpringSecurity. If you can't access svn: for some reason (stupid firewalls), then you can download a zip file from http://spring.habuma.com/examples/SpringSecurityMethods.zip. There's a Maven 2 pom.xml file in there--run the "test" goal to see the tests in action.

Craig Walls

About Craig Walls

Craig Walls is a Principal Engineer, Java Champion, Alexa Champion, and the author of Spring AI in Action, Spring in Action, and Build Talking Apps. He's a zealous promoter of the Spring Framework, speaking frequently at local user groups and conferences and writing about Spring. When he's not slinging code, Craig is planning his next trip to Disney World or Disneyland and spending as much time as he can with his wife, two daughters, 1 bird and 2 dogs.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »