[[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`) or the function must be a Kotlin coroutine function. This is necessary to integrate with Reactor's `Context`. ==== ==== .Java [source,java,role="primary"] ---- Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER"); Mono 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 = 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: ==== .Java [source,java,role="primary"] ---- Mono findMessageByUsername(String username) { return Mono.just("Hi " + username); } ---- .Kotlin [source,kotlin,role="secondary"] ---- fun findMessageByUsername(username: String): Mono { return Mono.just("Hi $username") } ---- ==== The following minimal method security configures method security in reactive applications: ==== .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: ==== .Java [source,java,role="primary"] ---- @Component public class HelloWorldMessageService { @PreAuthorize("hasRole('ADMIN')") public Mono findMessage() { return Mono.just("Hello World!"); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Component class HelloWorldMessageService { @PreAuthorize("hasRole('ADMIN')") fun findMessage(): Mono { return Mono.just("Hello World!") } } ---- ==== Alternatively, the following class uses Kotlin coroutines: ==== .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: ==== .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(exchanges -> exchanges .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].