spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.55k stars 5.79k forks source link

Allow the customization of the Reactive Method Security MethodSecurityExpressionHandler #7459

Open codependent opened 4 years ago

codependent commented 4 years ago

Summary

I need to customize the behavior of the reactive method security expression handler. In the non reactive version this could be done extending GlobalMethodSecurityConfiguration as stated in the documentation:

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        // ... create and return custom MethodSecurityExpressionHandler ...
        return expressionHandler;
    }
}

However, the reactive equivalent ReactiveMethodSecurityConfiguration is a package-private class and can't be extended to modify the framework.

Actual Behavior

We can't modify the expression handler.

Expected Behavior

Allow the definition of a custom MethodSecurityExpressionHandler

Configuration

...

Version

5.2.0.RC1

jnfeinstein commented 4 years ago

The reactive version is bean-based. Is this as simple as adding a conditional?

sunildabburi commented 3 years ago

I had a similar problem. I tried this approach and it worked.

Create the following configuration class that initializes the required security beans along with my custom expression handler bean.

class AuthorizationConfig {

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    DelegatingMethodSecurityMetadataSource methodMetadataSource(
            MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
        var attributeFactory = new ExpressionBasedAnnotationAttributeFactory(methodSecurityExpressionHandler);
        var prePostSource = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
        return new DelegatingMethodSecurityMetadataSource(List.of(prePostSource));
    }

    @Bean
    PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source,
            MethodSecurityExpressionHandler handler) {
        var postAdvice = new ExpressionBasedPostInvocationAdvice(handler);
        var preAdvice = new ExpressionBasedPreInvocationAdvice();
        preAdvice.setExpressionHandler(handler);
        return new PrePostAdviceReactiveMethodInterceptor(source, preAdvice, postAdvice);
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) {
        return new MethodSecurityMetadataSourceAdvisor("securityMethodInterceptor", source,
                "methodMetadataSource");
    }

    @Bean
    MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler(); // my custom expression handler
    }
}

DO NOT use @EnableReactiveMethodSecurity as the above code does what it does but not completely as the above implementation does not take into consideration the import order and default role prefixes which were not required in my case. If you want to use them too, you can clone the code from ReactiveMethodSecurityConfiguration

Hope this helps

ssozonoff commented 2 years ago

@Bean @Primary public DefaultMethodSecurityExpressionHandler getCustomMethodSecurityExpressionHandler() { return new CustomMethodSecurityExpressionHandler(); }

Seems to work as well

wickedev commented 2 years ago

How about avoiding this problem by calling setRoleHierarchy inside ReactiveMethodSecurityConfiguration.methodSecurityExpressionHandler method or adding @ConditionalOnMissingBean annotation?

jzheaux commented 2 years ago

Let's take a look at this after #9401 is merged.

LajosPolya commented 1 year ago

@bean @primary public DefaultMethodSecurityExpressionHandler getCustomMethodSecurityExpressionHandler() { return new CustomMethodSecurityExpressionHandler(); }

Seems to work as well

This worked for me but my CustomMethodSecurityExpressionHandler class has to be annotated with @Component and @Primary