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

  1. Firstly it says it will show @PreAuthorize and @PostAuthorize example and then the example is labeled as Only @PostAthorize Configuration

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);
    }
}
  1. 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);
    }
}