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
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:
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();
}
}
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With