There are few things you
need to know for securing your controller methods using security annotations.You will need to first configure global-method-security in you context
<sec:global-method-security
secured-annotations="enabled"
pre-post-annotations="enabled"/>
If you method
annotations are @Secured set secured-annotations=enabled and if you plan to use
@PreAuthorize or @PostAuthorize enable pre-post-annotations=enabled.
The placement of this
tag is also important, you need to place this tag in your mvc-context.xml
instead of security-context.xml, your mvc-context.xml is the one configured for the DispatcherServlet.
Web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/mvc-context.xml
/WEB-INF/spring/security-context.xml</param-value>
</context-param>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/mvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
There are suggestions to place global-method-security tag after component-scan tag in your context, but I didn't think it made any difference in my case.
<context:component-scan base-package="com.ironmountain.imconnect"
/>
<sec:global-method-security secured-annotations="enabled" pre-post-annotations="enabled"/>
After this, you might hit Add CGLIB to the class path exception
during initialization:
Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.
If so, you will need to add dependency
for CGLIB.
The reason you need CGLIB is that,
by default, Spring uses Java Dynamic Proxies to implement Aspect
Oriented Programming support. This requires you to "program to interfaces
rather than classes".
But we like to leverage
component-scan wiring for controllers using @Controller annotation, which means
controller are concrete classes and not implementation classes.
Meaning we need
to generate class level proxies for security annotated classes, which requires CGLIB
dependency.
So all this got my security annotation
to work for access denied scenarios, but for
success scenarios I still got this error after the method was properly
executed.
No mapping found for HTTP request with URI
[/imchome/app/users] in DispatcherServlet with name 'appServlet'
My controller was
declared as
@Controller
@RequestMapping(value = "/users")
public class ManageUsersController
implements InitializingBean
{
And I had configured
Tuckey’s URLrewriting filter to clean the URL from /imchome/app/users to
/imchome/users.
My Controller worked
perfectly fine, if I removed the secured annotations.
After hours of
debugging, I realized that the problem was that my controller was implementing
InitlizingBean interface.
By implementing InitlizingBean
interface my controller became an implementation class, which should be wired
through interface using spring’s dynamic proxy. Since I was creating the controller using
component-scan as a concerete class, it was giving problem.
My solution was to not
have my controller implement InitlizingBean.
So to get the security
annotation to work (Spring AOP), you either need to have an implementation class
created through its interface using Spring’s dynamic proxy or have a concrete
class which generates a dynamic class proxy using CGLIB. PS: even if you implement a blank interface, you will need to follow the first option.