mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-11-04 00:28:54 +00:00 
			
		
		
		
	This commit formats authorizeExchange blocks to use a common variable name and ensure the variable and reference are on the same line. Issue gh-13067
		
			
				
	
	
		
			676 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			676 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
[[jc-erms]]
 | 
						|
= EnableReactiveMethodSecurity
 | 
						|
 | 
						|
Spring Security supports method security by using https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context], which is set up by `ReactiveSecurityContextHolder`.
 | 
						|
The following example shows how to retrieve the currently logged in user's message:
 | 
						|
 | 
						|
[NOTE]
 | 
						|
====
 | 
						|
For this example to work, the return type of the method must be a `org.reactivestreams.Publisher` (that is, a `Mono` or a `Flux`).
 | 
						|
This is necessary to integrate with Reactor's `Context`.
 | 
						|
====
 | 
						|
 | 
						|
[[jc-enable-reactive-method-security-authorization-manager]]
 | 
						|
== EnableReactiveMethodSecurity with AuthorizationManager
 | 
						|
 | 
						|
In Spring Security 5.8, we can enable annotation-based security using the `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` annotation on any `@Configuration` instance.
 | 
						|
 | 
						|
This improves upon `@EnableReactiveMethodSecurity` in a number of ways. `@EnableReactiveMethodSecurity(useAuthorizationManager=true)`:
 | 
						|
 | 
						|
1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.
 | 
						|
This simplifies reuse and customization.
 | 
						|
2. Supports reactive return types including Kotlin coroutines.
 | 
						|
3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize
 | 
						|
4. Checks for conflicting annotations to ensure an unambiguous security configuration
 | 
						|
5. Complies with JSR-250
 | 
						|
 | 
						|
[NOTE]
 | 
						|
====
 | 
						|
For earlier versions, please read about similar support with <<jc-enable-reactive-method-security, @EnableReactiveMethodSecurity>>.
 | 
						|
====
 | 
						|
 | 
						|
For example, the following would enable Spring Security's `@PreAuthorize` annotation:
 | 
						|
 | 
						|
.Method Security Configuration
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@EnableReactiveMethodSecurity(useAuthorizationManager=true)
 | 
						|
public class MethodSecurityConfig {
 | 
						|
	// ...
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
 | 
						|
Spring Security's native annotation support defines a set of attributes for the method.
 | 
						|
These will be passed to the various method interceptors, like `AuthorizationManagerBeforeReactiveMethodInterceptor`, for it to make the actual decision:
 | 
						|
 | 
						|
.Method Security Annotation Usage
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
public interface BankService {
 | 
						|
	@PreAuthorize("hasRole('USER')")
 | 
						|
	Mono<Account> readAccount(Long id);
 | 
						|
 | 
						|
	@PreAuthorize("hasRole('USER')")
 | 
						|
	Flux<Account> findAccounts();
 | 
						|
 | 
						|
	@PreAuthorize("@func.apply(#account)")
 | 
						|
	Mono<Account> post(Account account, Double amount);
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
In this case `hasRole` refers to the method found in `SecurityExpressionRoot` that gets invoked by the SpEL evaluation engine.
 | 
						|
 | 
						|
`@bean` refers to a custom component you have defined, where `apply` can return `Boolean` or `Mono<Boolean>` to indicate the authorization decision.
 | 
						|
A bean like that might look something like this:
 | 
						|
 | 
						|
.Method Security Reactive Boolean Expression
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
public Function<Account, Mono<Boolean>> func() {
 | 
						|
    return (account) -> Mono.defer(() -> Mono.just(account.getId().equals(12)));
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
 | 
						|
[[jc-reactive-method-security-custom-granted-authority-defaults]]
 | 
						|
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 GrantedAuthorityDefaults
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 | 
						|
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
 | 
						|
	return new GrantedAuthorityDefaults("MYPREFIX_");
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
[TIP]
 | 
						|
====
 | 
						|
We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes.
 | 
						|
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<Boolean> {
 | 
						|
        // ... authorization logic
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Component("authz")
 | 
						|
open class AuthorizationLogic {
 | 
						|
    fun decide(val operations: MethodSecurityExpressionOperations): Mono<Boolean> {
 | 
						|
        // ... 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<String> endpoint() {
 | 
						|
        // ...
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Controller
 | 
						|
open class MyController {
 | 
						|
    @PreAuthorize("@authz.decide(#root)")
 | 
						|
    @GetMapping("/endpoint")
 | 
						|
    fun endpoint(): Mono<String> {
 | 
						|
        // ...
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
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<Boolean>`, 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<AuthorizationDecision> 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<AuthorizationDecision> {
 | 
						|
        // ... 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].
 | 
						|
 | 
						|
[[jc-reactive-method-security-custom-authorization-manager]]
 | 
						|
[[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<MethodInvocation> {
 | 
						|
    @Override
 | 
						|
    public Mono<AuthorizationDecision> check(Supplier<Authentication> authentication, MethodInvocation invocation) {
 | 
						|
        // ... authorization logic
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
class MyPreAuthorizeAuthorizationManager : ReactiveAuthorizationManager<MethodInvocation> {
 | 
						|
    override fun check(authentication: Supplier<Authentication>, invocation: MethodInvocation): Mono<AuthorizationDecision> {
 | 
						|
        // ... 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
 | 
						|
 | 
						|
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
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
[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.
 | 
						|
 | 
						|
== EnableReactiveMethodSecurity
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
 | 
						|
 | 
						|
Mono<String> messageByUsername = ReactiveSecurityContextHolder.getContext()
 | 
						|
	.map(SecurityContext::getAuthentication)
 | 
						|
	.map(Authentication::getName)
 | 
						|
	.flatMap(this::findMessageByUsername)
 | 
						|
	// In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`
 | 
						|
	.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
 | 
						|
 | 
						|
StepVerifier.create(messageByUsername)
 | 
						|
	.expectNext("Hi user")
 | 
						|
	.verifyComplete();
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
 | 
						|
 | 
						|
val messageByUsername: Mono<String> = ReactiveSecurityContextHolder.getContext()
 | 
						|
	.map(SecurityContext::getAuthentication)
 | 
						|
	.map(Authentication::getName)
 | 
						|
	.flatMap(this::findMessageByUsername) // In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`
 | 
						|
	.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))
 | 
						|
 | 
						|
StepVerifier.create(messageByUsername)
 | 
						|
	.expectNext("Hi user")
 | 
						|
	.verifyComplete()
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Where `this::findMessageByUsername` is defined as:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
Mono<String> findMessageByUsername(String username) {
 | 
						|
	return Mono.just("Hi " + username);
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
fun findMessageByUsername(username: String): Mono<String> {
 | 
						|
	return Mono.just("Hi $username")
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
The following minimal method security configures method security in reactive applications:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Configuration
 | 
						|
@EnableReactiveMethodSecurity
 | 
						|
public class SecurityConfig {
 | 
						|
	@Bean
 | 
						|
	public MapReactiveUserDetailsService userDetailsService() {
 | 
						|
		User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
 | 
						|
		UserDetails rob = userBuilder.username("rob")
 | 
						|
			.password("rob")
 | 
						|
			.roles("USER")
 | 
						|
			.build();
 | 
						|
		UserDetails admin = userBuilder.username("admin")
 | 
						|
			.password("admin")
 | 
						|
			.roles("USER","ADMIN")
 | 
						|
			.build();
 | 
						|
		return new MapReactiveUserDetailsService(rob, admin);
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Configuration
 | 
						|
@EnableReactiveMethodSecurity
 | 
						|
class SecurityConfig {
 | 
						|
	@Bean
 | 
						|
	fun userDetailsService(): MapReactiveUserDetailsService {
 | 
						|
		val userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()
 | 
						|
		val rob = userBuilder.username("rob")
 | 
						|
			.password("rob")
 | 
						|
			.roles("USER")
 | 
						|
			.build()
 | 
						|
		val admin = userBuilder.username("admin")
 | 
						|
			.password("admin")
 | 
						|
			.roles("USER", "ADMIN")
 | 
						|
			.build()
 | 
						|
		return MapReactiveUserDetailsService(rob, admin)
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Consider the following class:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
public class HelloWorldMessageService {
 | 
						|
	@PreAuthorize("hasRole('ADMIN')")
 | 
						|
	public Mono<String> findMessage() {
 | 
						|
		return Mono.just("Hello World!");
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
class HelloWorldMessageService {
 | 
						|
	@PreAuthorize("hasRole('ADMIN')")
 | 
						|
	fun findMessage(): Mono<String> {
 | 
						|
		return Mono.just("Hello World!")
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Alternatively, the following class uses Kotlin coroutines:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="primary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
class HelloWorldMessageService {
 | 
						|
    @PreAuthorize("hasRole('ADMIN')")
 | 
						|
    suspend fun findMessage(): String {
 | 
						|
        delay(10)
 | 
						|
        return "Hello World!"
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
 | 
						|
Combined with our configuration above, `@PreAuthorize("hasRole('ADMIN')")` ensures that `findByMessage` is invoked only by a user with the `ADMIN` role.
 | 
						|
Note that any of the expressions in standard method security work for `@EnableReactiveMethodSecurity`.
 | 
						|
However, at this time, we support only a return type of `Boolean` or `boolean` of the expression.
 | 
						|
This means that the expression must not block.
 | 
						|
 | 
						|
When integrating with xref:reactive/configuration/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Configuration
 | 
						|
@EnableWebFluxSecurity
 | 
						|
@EnableReactiveMethodSecurity
 | 
						|
public class SecurityConfig {
 | 
						|
 | 
						|
	@Bean
 | 
						|
	SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
 | 
						|
		return http
 | 
						|
			// Demonstrate that method security works
 | 
						|
			// Best practice to use both for defense in depth
 | 
						|
			.authorizeExchange((authorize) -> authorize
 | 
						|
				.anyExchange().permitAll()
 | 
						|
			)
 | 
						|
			.httpBasic(withDefaults())
 | 
						|
			.build();
 | 
						|
	}
 | 
						|
 | 
						|
	@Bean
 | 
						|
	MapReactiveUserDetailsService userDetailsService() {
 | 
						|
		User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
 | 
						|
		UserDetails rob = userBuilder.username("rob")
 | 
						|
			.password("rob")
 | 
						|
			.roles("USER")
 | 
						|
			.build();
 | 
						|
		UserDetails admin = userBuilder.username("admin")
 | 
						|
			.password("admin")
 | 
						|
			.roles("USER","ADMIN")
 | 
						|
			.build();
 | 
						|
		return new MapReactiveUserDetailsService(rob, admin);
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Configuration
 | 
						|
@EnableWebFluxSecurity
 | 
						|
@EnableReactiveMethodSecurity
 | 
						|
class SecurityConfig {
 | 
						|
	@Bean
 | 
						|
	open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 | 
						|
		return http {
 | 
						|
			authorizeExchange {
 | 
						|
				authorize(anyExchange, permitAll)
 | 
						|
			}
 | 
						|
			httpBasic { }
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	@Bean
 | 
						|
	fun userDetailsService(): MapReactiveUserDetailsService {
 | 
						|
		val userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()
 | 
						|
		val rob = userBuilder.username("rob")
 | 
						|
			.password("rob")
 | 
						|
			.roles("USER")
 | 
						|
			.build()
 | 
						|
		val admin = userBuilder.username("admin")
 | 
						|
			.password("admin")
 | 
						|
			.roles("USER", "ADMIN")
 | 
						|
			.build()
 | 
						|
		return MapReactiveUserDetailsService(rob, admin)
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
You can find a complete sample in {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method].
 |