2014-03-30

Spring Security: authorization on controller methods, part 2

In my previous post I showed a method to apply authorization rules to controller methods using annotations without moving <global-method-security> or @EnableGlobalMethodSecurity to web application context. My solution was something between web security and global method security. It operated on HTTP request level (just like web security), but it was configured by annotations (like global method security).

I improved my code from previous post, so it integrates with Spring Security better, and I packaged it as a library (spring-security-controller-auth). In this post I would like to describe this library.

Installation
The library is not in any maven repository, so you have to install it manually to your local repo:
git clone https://github.com/mateuszwenus/spring-security-controller-auth.git
cd spring-security-controller-auth
git checkout 0.9.0
mvn javadoc:jar source:jar install
Setup
After installation add the following dependency to your pom.xml:
<dependency>
  <groupId>com.github.mateuszwenus</groupId>
  <artifactId>spring-security-controller-auth</artifactId>
  <version>0.9.0</version>
</dependency>
Then you have to make some configuration. The central class in this library is HandlerSecurityInterceptor. It is a Spring MVC interceptor, which handles authrorization annotations. To use spring-security-controller-auth in your project you have to create and configure this interceptor. The simplest config looks like this:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() {
    try {
      return super.authenticationManagerBean();
    } catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }

  @Bean
  public HandlerSecurityInterceptor handlerSecurityInterceptor() {
    HandlerSecurityInterceptor interceptor = HandlerSecurityInterceptor.create();
    interceptor.setAuthenticationManager(authenticationManagerBean());
    return interceptor;
  }

  ...
}

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter

  @Autowired
  private WebSecurityConfig webSecurityConfig;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(webSecurityConfig.handlerSecurityInterceptor());
  }

  ...
}
The only not-so-obvious thing here is the overriden authenticationManagerBean() method. It is necessary because HandlerSecurityInterceptor requires an AuthenticationManager and by default WebSecurityConfigurerAdapter doesn't expose it as a bean (see javadoc for WebSecurityConfigurerAdapter.authenticationManagerBean() for more information).

After configuration, you can easily use it in your controllers, for example:
@AuthorizeRequest("hasRole('ROLE_USER')")
@RequestMapping("/index")
public String index() {
  ...
}

@AuthorizeRequest("hasRole('ROLE_ADMIN') and hasIpAddress('127.0.0.1')")
@RequestMapping("/admin")
public String admin() {
  ...
}
The AuthorizeRequest annotation accepts all web security expressions.

A more complex configuration and usage example can be found in my spring4-webbapp project. It shows how to configure this library with a RoleHierarchy.

Implementation details
The code of spring-security-controller-auth is based on code from my previous post, so I won't describe it here. I would only like to explain, how this library is better integrated with Spring Security than my previous solution.

In Spring Security the main entry point for making authorization decisions are the subclasses of AbstractSecurityInterceptor. There are three of them:
  • FilterSecurityInterceptor, which is responsible for web security
  • MethodSecurityInterceptor/AspectJMethodSecurityInterceptor which are responsible for global method security

This library adds a new subclass of AbstractSecurityInterceptor - HandlerSecurityInterceptor. The implementation of HandlerSecurityInterceptor is very similar to FilterSecurityInterceptor:
  • FilterSecurityInterceptor is a javax.servlet.Filter and it uses its doFilter() to intercept HTTP requests and to apply security rules to them (with the use of superclass'es methods).
  • Similarly, HandlerSecurityInterceptor is a HandlerInterceptor (Spring MVC interceptor) and it uses its preHandle()/afterCompletion() methods to intercept controller invocations and to make authorization decisions.

Every AbstractSecurityInterceptor has its SecurityMetadataSource which provides authorization rules for secured objects.
  • FilterSecurityInterceptor uses a FilterInvocationSecurityMetadataSource
  • MethodSecurityInterceptor/AspectJMethodSecurityInterceptor use a MethodSecurityMetadataSource
HandlerSecurityInterceptor is no exception and uses a HandlerInvocationSecurityMetadataSource. Its implementation is similar to implementations of MethodSecurityMetadataSource - it obtains authorization rules from annotations on secured methods.

I think this explains very well how and why my authorization mechanism is something between web security and global method security. It is implemented by two main components: HandlerSecurityInterceptor (which is similar to FilterSecurityInterceptor, responsible for web security) and HandlerInvocationSecurityMetadataSource (which is similar to MethodSecurityMetadataSource, used in global method security).

Summary
The library is available under MIT license, so you can use it any way you like. Please note, that it has not been thoroughly tested. If you encounter any problems, don't hesitate to let me know.