Can Spring Security Use @Preauthorize on Spring Controllers Methods

Can Spring Security use @PreAuthorize on Spring controllers methods?

Yes, it works fine.

You need <security:global-method-security pre-post-annotations="enabled" /> in ...-servlet.xml. It also requires CGLIB proxies, so either your controllers shouldn't have interfaces, or you should use proxy-target-class = true.

Spring Security @PreAuthorize on controllers

You cannot do that because ant matchers and @PreAuthorize work at different level.

The ant matchers works at http security level. Spring security filter looks at the request, and if it find that access should be denied, it does not even pass the request to the dispatcher servlet, and directly send a 403 error.

PreAuthorize work at method level. When a method is about to be called, an AOP proxy controls if the access should be allowed. So the 2 authorizations level are chained, instead of the second overriding the first.

Anyway, I strongly advice you not to use @PreAuthorize("hasRole('ADMIN')") on a controller :

  • it can easily be done with a simple ant matcher
  • it forces you to allow proxying on a controller, either with class proxying instead of JDK proxying or by using interfaces for controllers

IMHO, @PreAuthorize is best suited at service level, because you can mix domain objects with user granted authorities to get fine grained authorizations.

With which methods does @Secured and @PreAuthorize annotation work?

No, not because it's private, but because Spring-Security is based on Spring-AOP.
On Spring-AOP, the call between methods that are in the same classes won't call aspects.

With @Secured annotation, a test is made before the method. If the user hasn't the right roles, an exception is thrown.

@PreAuthorize is practically the same, except it allows more advanced behavior.

You can also configure security using WebSecurityConfigurerAdapter.
And do not forget to enable the Pre/post annotations with @EnableGlobalMethodSecurity(prePostEnabled = true)

Spring Security: @PreAuthorize works only together with @RequestMapping

In the second example the test method is being called directly from the handleRequest method. Spring has no mechanism to intercept method calls from with in the same class. Thus, the Proxy / AOP method inception for @PreAutorize is never invoked.

More on the topic of Spring Proxy

Using @PreAuthorize annotation on a method within a controller’s method

When you call methodController() directly from exampleForMethodPreAuthorize(), the call if not invoked through a proxy. Only proxy calls are enhanced with annotated behaviour.

Credits to this answer: https://stackoverflow.com/a/28168213/1849366. It also suggests two workarounds.

Option 1: Moving methodController() method to another bean.

Option 2: Call the method methodController() through a proxy using as follows.

@GetMapping(value= "/method")
public String exampleForMethodPreAuthorize() {
if(context.getBean(MyController.class).methodController()){
return "forMethodPreAuthorize";
}
else return null;
}

Spring Security: Deny access to controller methods, if @PreAuthorize annotation is missing

I'm answering my own question here.

I've solved the problem by using a HandlerInterceptorAdapter.

I'm not sure it is the most Spring-idiomatic way to achieve the result, but it's good enough for me.

public class MvcPreAuthorizeAnnotationCheckerInterceptor extends HandlerInterceptorAdapter {
final HandlerMethod hm;
if (handler instanceof HandlerMethod) {
hm = (HandlerMethod) handler;
PreAuthorize annotation = hm.getMethodAnnotation(PreAuthorize.class);
if (annotation == null) {
// check if the class is annotated...
annotation = hm.getMethod().getDeclaringClass().getAnnotation(PreAuthorize.class);
if (annotation == null) {
// add logging
// or send a NON AUTHORIZED
response.sendRedirect(request.getContextPath());
}
}
return true;
}
}

And in the Spring config:

<mvc:interceptors>
<beans:ref bean="mvcPreAuthorizeAnnotationCheckerInterceptor"/>
</mvc:interceptors>

<beans:bean id="mvcPreAuthorizeAnnotationCheckerInterceptor" class="com.acme.MvcPreAuthorizeAnnotationCheckerInterceptor"/>

PreAuthorize not working on Controller

A common problem with using PrePost annotations on controllers is that Spring method security is based on Spring AOP, which is by default implemented with JDK proxies.

That means that it works fine on the service layer which is injected in controller layer as interfaces, but it is ignored on controller layer because controller generally do not implement interfaces.

The following is just my opinion:

  • prefered way: move the pre post annotation on service layer
  • if you cannot (or do not want to), try to have your controller implement an interface containing all the annotated methods
  • as a last way, use proxy-target-class=true


Related Topics



Leave a reply



Submit