Been looking into the documentation trying to figure out how to use a custom AuthorizationManager with PreAuthorize and I think I've found a few issues in the documentation. https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#custom-authorization-managers
- Firstly it says it will show
@PreAuthorizeand@PostAuthorizeexample and then the example is labeled as Only@PostAthorizeConfiguration
Confusing/wrong part: Then, publish the method interceptor with a pointcut that corresponds to when you want that AuthorizationManager to run. For example, you could replace how @PreAuthorize and @PostAuthorize work like so:
Only @PostAuthorize Configuration ...CODE example...
Then the example given has 2 methods named the same way (postAuthorize) that have the same number of parameters. Then inside, what I'm assuming, is that this configures both the preAuthorize and postAuthorize.
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorize(MyAuthorizationManager manager) {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorize(MyAuthorizationManager manager) {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
}
}
- The example as a whole won't compile (even after the issues above are fixed). This is the code I'm trying to run.
@Component
public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
return new AuthorizationDecision(false);
}
}
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorize(MyAuthorizationManager manager) {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorize(MyAuthorizationManager manager) {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager); //Exception here
}
}
This is the exception. From what I can undestand the postAuthorize() method is expecting AuthorizationManager<MethodInvocationResult> and can't compile since the example one is using ...<MethodInvocation>.
java: no suitable method found for postAuthorize(com.company.projectManager.common.security.config.MyAuthorizationManager) method org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor.postAuthorize(org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager) is not applicable (argument mismatch; com.company.projectManager.common.security.config.MyAuthorizationManager cannot be converted to org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager) method org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor.postAuthorize(org.springframework.security.authorization.AuthorizationManager<org.springframework.security.authorization.method.MethodInvocationResult>) is not applicable (argument mismatch; com.company.projectManager.common.security.config.MyAuthorizationManager cannot be converted to org.springframework.security.authorization.AuthorizationManager<org.springframework.security.authorization.method.MethodInvocationResult>)
Sorry if this looks like a nitpick to you. I just found it really confusing trying to read through this as a newbie.
Comment From: marcusdacoregio
Hi, @pbborisov18. Thanks for the report.
I agree that it can be confusing, the problem is that AuthorizationManagerAfterMethodInterceptor.postAuthorize(...) expects a AuthorizationManager<MethodInvocationResult> or a PostAuthorizeAuthorizationManager. In order to use the MyAuthorizationManager in both preAuthorize and postAuthorize, you would need to implement both interfaces:
@Component
public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
return new AuthorizationDecision(false);
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) {
return new AuthorizationDecision(false);
}
}
Are you interested in contributing by applying that change to the documentation? Ideally, you should open a PR that adds the AuthorizationManager<MethodInvocationResult> interface to MyAuthenticationManager and targets the 6.1.x branch.
Comment From: marcusdacoregio
Closing in favor of https://github.com/spring-projects/spring-security/pull/13991
Comment From: MEnnabah
In order to use the MyAuthorizationManager in both preAuthorize and postAuthorize, you would need to implement both interfaces: ```java @Component public class MyAuthorizationManager implements AuthorizationManager
, AuthorizationManager { @Override public AuthorizationDecision check(Supplier authentication, MethodInvocation invocation) { return new AuthorizationDecision(false); } @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) { return new AuthorizationDecision(false); }} ```
This won't work either. To the compiler, the AuthorizationManager interface is implemented twice, despite having two completely different generic types. When attempted, the following compile-time error is raised:
error: repeated interface
public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {
^
Comment From: pbborisov18
You are absolutely right. Big mistake on my part for not noticing it. I guess splitting it into two classes - MyPreAuthorizeManager and MyPostAuthorizeManager should fix the issue. Any input on that?
Comment From: abccbaandy
Does this issue not fixed yet? The code in the latest reference still wrong.
https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#custom-authorization-managers
@Component
public class MyAuthorizationManager implements AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
// ... authorization logic
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) {
// ... authorization logic
}
}
Or maybe it can be like this?
public class MyAuthorizationManager<T> implements AuthorizationManager<T> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
boolean result = false;
if (object instanceof MethodInvocationResult mir) {
// ... authorization logic
} else if (object instanceof MethodInvocation mi) {
// ... authorization logic
}
return new AuthorizationDecision(result);
}
}