Saturday, October 27, 2012

Spring MVC + Security: @Secured annotation ignored

Quick note from the fields (solution at the bottom).

Currently working on Spring MVC (3.1.2) with Spring Security (3.1.1) and want my @Controller actions to be processed by Security in way that user must have specific role to be able to invoke certain actions. For that case I want to use @Secured annotation, like this:

@Secured({ "ROLE_USER" })
@RequestMapping(method = RequestMethod.GET, value = "change")
public String getPasswordChangeForm(Model model, HttpServletRequest request) {
 // ...
 return Views.CHANGE_PASSWORD;
}

Sounds pretty simple? Yes. Clear how to implement? Defensively no.
Spring security documentation gives us only little hint on this, why it may not work:
The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled). If you want to secure instances which are not created by Spring (using the new operator, for example) then you need to use AspectJ.
Did you notice this little phrase in braces: "in the same application context in which method-security is enabled"? Good! Moving forward they definitely should move some sections (like this) from the FAQ to the documentation.

Somewhere else in Spring Security documentation there is a statement that you just need to add <sec:global-method-security secured-annotations="enabled" /> to you security-context.xml and magic will happen, @Secured annotation will then take effect. This is partially true, Our Controller still not being affected.

Why? Because it's in servlet context, but security is normally configured in application context (SecuredAnnotationSecurityMetadataSource even will not be called for our controllers). And this part might be very tricky because most likely you don't want to mix servlet context and application context you only want this security thing to take effect.

After several hours of fighting with this stuff I hit "object is not an instance of declaring class" exception which led me to the solution (thank you StackOverflow and people who contribute their knowledge!):

Solution:
- Assuming you have ContextLoaderListener configured so servlet-context have access to beans from application-context, simply put only <sec:global-method-security secured-annotations="enabled" proxy-target-class="true" /> to the servlet-context
- Also you'd need to add cglib and spring-asm to the WEB-INF/lib

1 comment:

Spring MVC + Security: @Secured annotation ignored

Quick note from the fields (solution at the bottom). Currently working on Spring MVC (3.1.2) with Spring Security (3.1.1) and want my @Con...