From 60cd8fdc552a340d9b7f7b7085c6e0a7107ecb0b Mon Sep 17 00:00:00 2001 From: DingHao Date: Sun, 1 Sep 2024 08:59:06 +0800 Subject: [PATCH 1/3] Support custom MethodSecurityExpressionHandler Closes gh-15715 --- ...ionManagerMethodSecurityConfiguration.java | 2 + ...ctiveMethodSecurityConfigurationTests.java | 44 ++++++++++++++ .../ReactiveMethodSecurityService.java | 3 + .../ReactiveMethodSecurityServiceImpl.java | 5 ++ .../pages/reactive/authorization/method.adoc | 59 ++++++++++++++++++- 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java index 8e777e8bc4c..6acd05d2f4d 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Fallback; import org.springframework.context.annotation.Role; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; @@ -114,6 +115,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor( @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + @Fallback static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler( @Autowired(required = false) GrantedAuthorityDefaults grantedAuthorityDefaults) { DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostReactiveMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostReactiveMethodSecurityConfigurationTests.java index 5fe335870d7..6fb3441ff72 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostReactiveMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostReactiveMethodSecurityConfigurationTests.java @@ -16,14 +16,22 @@ package org.springframework.security.config.annotation.method.configuration; +import java.io.Serializable; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import reactor.test.StepVerifier; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.authorization.AuthorizationDeniedException; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; +import org.springframework.security.core.Authentication; import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -201,6 +209,17 @@ void preAuthorizeWhenAllowedAndHandlerWithCustomAnnotationUsingBeanThenInvokeMet StepVerifier.create(service.preAuthorizeWithMaskAnnotationUsingBean()).expectNext("ok").verifyComplete(); } + @Test + @WithMockUser(roles = "ADMIN") + public void customMethodSecurityExpressionHandler() { + this.spring.register(MethodSecurityServiceEnabledConfig.class, PermissionEvaluatorConfig.class).autowire(); + ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class); + StepVerifier.create(service.preAuthorizeHasPermission("grant")).expectNext("ok").verifyComplete(); + StepVerifier.create(service.preAuthorizeHasPermission("deny")) + .expectError(AuthorizationDeniedException.class) + .verify(); + } + @Configuration @EnableReactiveMethodSecurity static class MethodSecurityServiceEnabledConfig { @@ -212,4 +231,29 @@ ReactiveMethodSecurityService methodSecurityService() { } + @Configuration + static class PermissionEvaluatorConfig { + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() { + DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); + handler.setPermissionEvaluator(new PermissionEvaluator() { + @Override + public boolean hasPermission(Authentication authentication, Object targetDomainObject, + Object permission) { + return "grant".equals(targetDomainObject); + } + + @Override + public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, + Object permission) { + throw new UnsupportedOperationException(); + } + }); + return handler; + } + + } + } diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityService.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityService.java index 000dcb386a0..e2c3bef113d 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityService.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityService.java @@ -101,6 +101,9 @@ public interface ReactiveMethodSecurityService { @HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class) Mono checkCustomResult(boolean result); + @PreAuthorize("hasPermission(#kgName, 'read')") + Mono preAuthorizeHasPermission(String kgName); + class StarMaskingHandler implements MethodAuthorizationDeniedHandler { @Override diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityServiceImpl.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityServiceImpl.java index acf50eb1130..3787556a878 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityServiceImpl.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityServiceImpl.java @@ -88,4 +88,9 @@ public Mono checkCustomResult(boolean result) { return Mono.just("ok"); } + @Override + public Mono preAuthorizeHasPermission(String kgName) { + return Mono.just("ok"); + } + } diff --git a/docs/modules/ROOT/pages/reactive/authorization/method.adoc b/docs/modules/ROOT/pages/reactive/authorization/method.adoc index cfc4a35cad3..2f1601db397 100644 --- a/docs/modules/ROOT/pages/reactive/authorization/method.adoc +++ b/docs/modules/ROOT/pages/reactive/authorization/method.adoc @@ -97,7 +97,7 @@ Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFil Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`. You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so: -.Custom MethodSecurityExpressionHandler +.Custom GrantedAuthorityDefaults [tabs] ====== Java:: @@ -118,6 +118,63 @@ We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spri Since the `GrantedAuthorityDefaults` bean is part of internal workings of Spring Security, we should also expose it as an infrastructural bean effectively avoiding some warnings related to bean post-processing (see https://github.com/spring-projects/spring-security/issues/14751[gh-14751]). ==== +[[customizing-expression-handling]] +=== Customizing Expression Handling + +Or, third, you can customize how each SpEL expression is handled. +To do that, you can expose a custom `MethodSecurityExpressionHandler`, like so: + +.Custom MethodSecurityExpressionHandler +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Bean +static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) { + DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); + handler.setRoleHierarchy(roleHierarchy); + return handler; +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +companion object { + @Bean + fun methodSecurityExpressionHandler(val roleHierarchy: RoleHierarchy) : MethodSecurityExpressionHandler { + val handler = DefaultMethodSecurityExpressionHandler() + handler.setRoleHierarchy(roleHierarchy) + return handler + } +} +---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + + + + + + + +---- +====== + +[TIP] +==== +We expose `MethodSecurityExpressionHandler` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes +==== + +You can also subclass xref:servlet/authorization/method-security.adoc#subclass-defaultmethodsecurityexpressionhandler[`DefaultMessageSecurityExpressionHandler`] to add your own custom authorization expressions beyond the defaults. + [[jc-reactive-method-security-custom-authorization-manager]] === Custom Authorization Managers From 756d00b7e52d7d7b52d302c62511a4f54421c3b3 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 10 Sep 2024 08:25:56 -0600 Subject: [PATCH 2/3] Document Programmatic Authorization in Reactive --- .../pages/reactive/authorization/method.adoc | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/docs/modules/ROOT/pages/reactive/authorization/method.adoc b/docs/modules/ROOT/pages/reactive/authorization/method.adoc index 2f1601db397..586fd1214f8 100644 --- a/docs/modules/ROOT/pages/reactive/authorization/method.adoc +++ b/docs/modules/ROOT/pages/reactive/authorization/method.adoc @@ -118,6 +118,215 @@ We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spri Since the `GrantedAuthorityDefaults` bean is part of internal workings of Spring Security, we should also expose it as an infrastructural bean effectively avoiding some warnings related to bean post-processing (see https://github.com/spring-projects/spring-security/issues/14751[gh-14751]). ==== +[[use-programmatic-authorization]] +== Authorizing Methods Programmatically + +As you've already seen, there are several ways that you can specify non-trivial authorization rules using xref:servlet/authorization/method-security.adoc#authorization-expressions[Method Security SpEL expressions]. + +There are a number of ways that you can instead allow your logic to be Java-based instead of SpEL-based. +This gives use access the entire Java language for increased testability and flow control. + +=== Using a Custom Bean in SpEL + +The first way to authorize a method programmatically is a two-step process. + +First, declare a bean that has a method that takes a `MethodSecurityExpressionOperations` instance like the following: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Component("authz") +public class AuthorizationLogic { + public decide(MethodSecurityExpressionOperations operations): Mono { + // ... authorization logic + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Component("authz") +open class AuthorizationLogic { + fun decide(val operations: MethodSecurityExpressionOperations): Mono { + // ... authorization logic + } +} +---- +====== + +Then, reference that bean in your annotations in the following way: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Controller +public class MyController { + @PreAuthorize("@authz.decide(#root)") + @GetMapping("/endpoint") + public Mono endpoint() { + // ... + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Controller +open class MyController { + @PreAuthorize("@authz.decide(#root)") + @GetMapping("/endpoint") + fun endpoint(): Mono { + // ... + } +} +---- +====== + +Spring Security will invoke the given method on that bean for each method invocation. + +What's nice about this is all your authorization logic is in a separate class that can be independently unit tested and verified for correctness. +It also has access to the full Java language. + +[TIP] +In addition to returning a `Mono`, you can also return `Mono.empty()` to indicate that the code abstains from making a decision. + +If you want to include more information about the nature of the decision, you can instead return a custom `AuthorizationDecision` like this: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Component("authz") +public class AuthorizationLogic { + public Mono decide(MethodSecurityExpressionOperations operations) { + // ... authorization logic + return Mono.just(new MyAuthorizationDecision(false, details)); + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Component("authz") +open class AuthorizationLogic { + fun decide(val operations: MethodSecurityExpressionOperations): Mono { + // ... authorization logic + return Mono.just(MyAuthorizationDecision(false, details)) + } +} +---- +====== + +Or throw a custom `AuthorizationDeniedException` instance. +Note, though, that returning an object is preferred as this doesn't incur the expense of generating a stacktrace. + +Then, you can access the custom details when you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[customize how the authorization result is handled]. + +[[custom-authorization-managers]] +=== Using a Custom Authorization Manager + +The second way to authorize a method programmatically is to create a custom xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[`AuthorizationManager`]. + +First, declare an authorization manager instance, perhaps like this one: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Component +public class MyPreAuthorizeAuthorizationManager implements ReactiveAuthorizationManager { + @Override + public Mono check(Supplier authentication, MethodInvocation invocation) { + // ... authorization logic + } + +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Component +class MyPreAuthorizeAuthorizationManager : ReactiveAuthorizationManager { + override fun check(authentication: Supplier, invocation: MethodInvocation): Mono { + // ... authorization logic + } + +} +---- +====== + +Then, publish the method interceptor with a pointcut that corresponds to when you want that `ReactiveAuthorizationManager` to run. +For example, you could replace how `@PreAuthorize` and `@PostAuthorize` work like so: + +.Only @PreAuthorize and @PostAuthorize Configuration +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableMethodSecurity(prePostEnabled = false) +class MethodSecurityConfig { + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + Advisor preAuthorize(MyPreAuthorizeAuthorizationManager manager) { + return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(manager); + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + Advisor postAuthorize(MyPostAuthorizeAuthorizationManager manager) { + return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(manager); + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Configuration +@EnableMethodSecurity(prePostEnabled = false) +class MethodSecurityConfig { + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + fun preAuthorize(val manager: MyPreAuthorizeAuthorizationManager) : Advisor { + return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(manager) + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + fun postAuthorize(val manager: MyPostAuthorizeAuthorizationManager) : Advisor { + return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(manager) + } +} +---- +====== + +[TIP] +==== +You can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`. +==== + [[customizing-expression-handling]] === Customizing Expression Handling From c6f40701d4b99a6487ed756607ef431190f140eb Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 10 Sep 2024 12:40:19 -0600 Subject: [PATCH 3/3] Rework Method Security Reactive Docs --- .../pages/reactive/authorization/method.adoc | 256 ++++-------------- 1 file changed, 56 insertions(+), 200 deletions(-) diff --git a/docs/modules/ROOT/pages/reactive/authorization/method.adoc b/docs/modules/ROOT/pages/reactive/authorization/method.adoc index 586fd1214f8..42a68da6093 100644 --- a/docs/modules/ROOT/pages/reactive/authorization/method.adoc +++ b/docs/modules/ROOT/pages/reactive/authorization/method.adoc @@ -88,6 +88,61 @@ public Function> func() { ---- ====== +Method authorization is a combination of before- and after-method authorization. + +[NOTE] +==== +Before-method authorization is performed before the method is invoked. +If that authorization denies access, the method is not invoked, and an `AccessDeniedException` is thrown. +After-method authorization is performed after the method is invoked, but before the method returns to the caller. +If that authorization denies access, the value is not returned, and an `AccessDeniedException` is thrown +==== + +To recreate what adding `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` does by default, you would publish the following configuration: + +.Full Pre-post Method Security Configuration +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +class MethodSecurityConfig { + @Bean + BeanDefinitionRegistryPostProcessor aopConfig() { + return AopConfigUtils::registerAutoProxyCreatorIfNecessary; + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor() { + return new PreFilterAuthorizationReactiveMethodInterceptor(); + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor() { + return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(); + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor() { + return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(); + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor() { + return new PostFilterAuthorizationReactiveMethodInterceptor(); + } +} +---- +====== + +Notice that Spring Security's method security is built using Spring AOP. + === Customizing Authorization Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support. @@ -236,6 +291,7 @@ Note, though, that returning an object is preferred as this doesn't incur the ex Then, you can access the custom details when you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[customize how the authorization result is handled]. +[[jc-reactive-method-security-custom-authorization-manager]] [[custom-authorization-managers]] === Using a Custom Authorization Manager @@ -361,20 +417,6 @@ companion object { } } ---- - -Xml:: -+ -[source,xml,role="secondary"] ----- - - - - - - - ----- ====== [TIP] @@ -384,192 +426,6 @@ We expose `MethodSecurityExpressionHandler` using a `static` method to ensure th You can also subclass xref:servlet/authorization/method-security.adoc#subclass-defaultmethodsecurityexpressionhandler[`DefaultMessageSecurityExpressionHandler`] to add your own custom authorization expressions beyond the defaults. -[[jc-reactive-method-security-custom-authorization-manager]] -=== Custom Authorization Managers - -Method authorization is a combination of before- and after-method authorization. - -[NOTE] -==== -Before-method authorization is performed before the method is invoked. -If that authorization denies access, the method is not invoked, and an `AccessDeniedException` is thrown. -After-method authorization is performed after the method is invoked, but before the method returns to the caller. -If that authorization denies access, the value is not returned, and an `AccessDeniedException` is thrown -==== - -To recreate what adding `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` does by default, you would publish the following configuration: - -.Full Pre-post Method Security Configuration -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -class MethodSecurityConfig { - @Bean - BeanDefinitionRegistryPostProcessor aopConfig() { - return AopConfigUtils::registerAutoProxyCreatorIfNecessary; - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor() { - return new PreFilterAuthorizationReactiveMethodInterceptor(); - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor() { - return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(); - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor() { - return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(); - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor() { - return new PostFilterAuthorizationReactiveMethodInterceptor(); - } -} ----- -====== - -Notice that Spring Security's method security is built using Spring AOP. -So, interceptors are invoked based on the order specified. -This can be customized by calling `setOrder` on the interceptor instances like so: - -.Publish Custom Advisor -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Bean -@Role(BeanDefinition.ROLE_INFRASTRUCTURE) -Advisor postFilterAuthorizationMethodInterceptor() { - PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor(); - interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1); - return interceptor; -} ----- -====== - -You may want to only support `@PreAuthorize` in your application, in which case you can do the following: - -.Only @PreAuthorize Configuration -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -class MethodSecurityConfig { - @Bean - BeanDefinitionRegistryPostProcessor aopConfig() { - return AopConfigUtils::registerAutoProxyCreatorIfNecessary; - } - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - Advisor preAuthorize() { - return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(); - } -} ----- -====== - -Or, you may have a custom before-method `ReactiveAuthorizationManager` that you want to add to the list. - -In this case, you will need to tell Spring Security both the `ReactiveAuthorizationManager` and to which methods and classes your authorization manager applies. - -Thus, you can configure Spring Security to invoke your `ReactiveAuthorizationManager` in between `@PreAuthorize` and `@PostAuthorize` like so: - -.Custom Before Advisor - -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@EnableReactiveMethodSecurity(useAuthorizationManager=true) -class MethodSecurityConfig { - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public Advisor customAuthorize() { - JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut(); - pattern.setPattern("org.mycompany.myapp.service.*"); - ReactiveAuthorizationManager rule = AuthorityAuthorizationManager.isAuthenticated(); - AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(pattern, rule); - interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1); - return interceptor; - } -} ----- -====== - -[TIP] -==== -You can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`. -==== - -The same can be done for after-method authorization. -After-method authorization is generally concerned with analysing the return value to verify access. - -For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so: - -.@PostAuthorize example -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -public interface BankService { - - @PreAuthorize("hasRole('USER')") - @PostAuthorize("returnObject.owner == authentication.name") - Mono readAccount(Long id); -} ----- -====== - -You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated. - -For example, if you have your own custom annotation, you can configure it like so: - - -.Custom After Advisor -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@EnableReactiveMethodSecurity(useAuthorizationManager=true) -class MethodSecurityConfig { - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public Advisor customAuthorize(ReactiveAuthorizationManager rules) { - AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class); - AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(pattern, rules); - interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1); - return interceptor; - } -} ----- -====== - -and it will be invoked after the `@PostAuthorize` interceptor. - == EnableReactiveMethodSecurity [tabs]