Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a web application configured to use Spring Security 3.2 in standard way.

I'm using the @PreAuthorize annotation to secure the Controllers method. Now, I would like to deny access to each controller method UNLESS it is annotated with @PreAuthorize.

I have tried the following approaches:

super controller

Each controller extends from a super controller annotated with: @PreAutorize("denyAll"). This approach doesn't seem to work because the controllers' methods annotations are ignored. Everything is forbidden.

@PreAutorize("denyAll") 
public class SuperController {

}

public class MyController extends SuperController {

    @PreAuthorize("hasRole('SUPERHERO')")
    @RequestMapping(value = URL_PREFIX + "Add.do", method =  RequestMethod.GET)
    public String doStuff(Model model) {

        ...
    }

}

aop

Using a pointcut expression in the global method security tag

<global-method-security pre-post-annotations="enabled">
    <protect-pointcut expression="execution(* com.acme.*Controller.*(..))" access="denyAll" />
 </global-method-security>

This approach also fails: controllers's methods which are not annotated are still accessible.

like image 810
Luciano Fiandesio Avatar asked May 20 '15 11:05

Luciano Fiandesio


People also ask

What is the use of @PreAuthorize annotation?

The @PreAuthorize annotation checks the given expression before entering the method, whereas the @PostAuthorize annotation verifies it after the execution of the method and could alter the result.

Which annotation can be used with Spring Security to apply method level security?

Method-level security is implemented by placing the @PreAuthorize annotation on controller methods (actually one of a set of annotations available, but the most commonly used). This annotation contains a Spring Expression Language (SpEL) snippet that is assessed to determine if the request should be authenticated.

How does @PreAuthorize work in spring boot?

Spring Security provides method level security using @PreAuthorize and @PostAuthorize annotations. This is expression-based access control. The @PreAuthorize can check for authorization before entering into method. The @PreAuthorize authorizes on the basis of role or the argument which is passed to the method.

What's the difference between @secured and @PreAuthorize in Spring Security?

The difference between @Secured and @PreAuthorize are as follows : The main difference between @Secured and @PreAuthorize is that @PreAuthorize can work with Spring EL. We can access methods and properties of SecurityExpressionRoot while using @PreAuthorize but not with @Secured.


2 Answers

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"/>
like image 112
Luciano Fiandesio Avatar answered Sep 18 '22 12:09

Luciano Fiandesio


I came up with a similar approach, but its not executed for every request but just extends the ConfigAttribute's for the method:

A minor drawback could be that it does not allow for easy logging or whatever the big benefit is that it follows the same deny behavior as other not allowed endpoints.

SecurityConfig:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        return new CustomPermissionAllowedMethodSecurityMetadataSource();
    }
}

MetadataSource:

public class CustomPermissionAllowedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
    @Override
    protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
        return null;
    }

    @Override
    protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
        Annotation[] annotations = AnnotationUtils.getAnnotations(method);
        List<ConfigAttribute> attributes = new ArrayList<>();

        // if the class itself is annotated as a @Controller we should by default deny access to every method
        if (AnnotationUtils.findAnnotation(targetClass, Controller.class) != null) {
            attributes.add(DENY_ALL_ATTRIBUTE);
        }

        if (annotations != null) {
            for (Annotation a : annotations) {
                // but not if the method has at least a PreAuthorize or PostAuthorize annotation
                if (a instanceof PreAuthorize || a instanceof PostAuthorize) {
                    return null;
                }
            }
        }
        return attributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
}

I also wrote a small article about this, with some further background: https://www.baeldung.com/spring-deny-access

like image 34
Martin van Wingerden Avatar answered Sep 17 '22 12:09

Martin van Wingerden