Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotated class with @Secured and @PreAuthorize does not secure super methods

Situation
I have created an CrudController which contains some basic crud methods. This works fine. When i want to secure this controller i can override the method from the super class and that works fine. However i don't want to override every method just for security. I want to use @Secured or @PreAuthorize on the class for that.

I'm using Spring Boot 2.2.2 (latest at time of writing).

Example
Crud base class

public class CrudController<T, ID> {
    @GetMapping
    public ResponseEntity<List<T>> findAll() {
        // code for returning the list
    }
}

Implementing class

@RestController
@RequestMapping("/helloworld")
@Secured("ROLE_world.view") 
public class HelloWorld extends CrudController<World, Long> {

   @GetMapping("test")
   public ResponseEntity test() {
      // This method is secured by the annotation on the class
   }

   // the findAll from the super class is not secured

}

Expected behaviour
When a class is annotated with either @PreAuthorize or @Secured i expect that all public methods are secured. So also all public methods from the extended class should be secured. In my example findAll is insecure and that is an high security risk.

Tried
I already have tried setting the folowing annotations for configuration:

On the class itself:

@Scope( proxyMode = ScopedProxyMode.TARGET_CLASS )

On the configuration class:

@Configuration
@EnableAspectJAutoProxy
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true,
        mode = AdviceMode.PROXY,
        proxyTargetClass = true
)

In the application.properties:

spring.aop.proxy-target-class=true
spring.aop.auto=true
like image 991
Martin Diphoorn Avatar asked Nov 19 '25 12:11

Martin Diphoorn


1 Answers

Secured annotation is handled in the class AbstractFallbackMethodSecurityMetadataSource, in the following method:

public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
        // The method may be on an interface, but we need attributes from the target
        // class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        // First try is the method in the target class.
        Collection<ConfigAttribute> attr = findAttributes(specificMethod, targetClass);
        if (attr != null) {
            return attr;
        }

        // Second try is the config attribute on the target class.
        attr = findAttributes(specificMethod.getDeclaringClass());
        if (attr != null) {
            return attr;
        }

        if (specificMethod != method || targetClass == null) {
            // Fallback is to look at the original method.
            attr = findAttributes(method, method.getDeclaringClass());
            if (attr != null) {
                return attr;
            }
            // Last fallback is the class of the original method.
            return findAttributes(method.getDeclaringClass());
        }
        return Collections.emptyList();
}

So if the method does not have @Secured annotation, the annotation is searched in the declaring class findAttributes(method.getDeclaringClass()), for your case the declaring class for findAll is CrudController that does not have any Secured annotation.

To solve this issue you should provide your own implementation of MethodSecurityMetadataSource.

Here after a simple way to do it:

  1. Define a custom implementation of MethodSecurityMetadataSource

    public class CustomMethodSecurityMetadaSource extends
        SecuredAnnotationSecurityMetadataSource {
    
        public CustomMethodSecurityMetadaSource(){
            super();
        }
        @Override
        public Collection<ConfigAttribute> getAttributes(Method method,
        Class<?> targetClass) {
            Collection<ConfigAttribute> attr =  super.getAttributes(method, targetClass);
            if (CollectionUtils.isEmpty(attr)) {
                attr = findAttributes(targetClass);
                if (attr != null) return attr;
            }
            return Collections.emptyList();
        }
    }
    
  2. Inject the custom implemetation by extending GlobalMethodSecurityConfiguration:

    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    @Configuration
    public class SecuredConfig extends GlobalMethodSecurityConfiguration {
        @Override
        protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
            return new CustomMethodSecurityMetadaSource();
        }
    }
    
like image 103
hasnae Avatar answered Nov 22 '25 01:11

hasnae