| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 | [[jc-erms]]= EnableReactiveMethodSecuritySpring Security supports method security using https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context] which is setup using `ReactiveSecurityContextHolder`.For example, this demonstrates how to retrieve the currently logged in user's message.[NOTE]====For this to work the return type of the method must be a `org.reactivestreams.Publisher` (i.e. `Mono`/`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<String> messageByUsername = ReactiveSecurityContextHolder.getContext()	.map(SecurityContext::getAuthentication)	.map(Authentication::getName)	.flatMap(this::findMessageByUsername)	// In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`	.subscriberContext(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`	.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication))StepVerifier.create(messageByUsername)	.expectNext("Hi user")	.verifyComplete()----====with `this::findMessageByUsername` defined as:====.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")}----====Below is a minimal method security configuration when using method security in reactive applications.====.Java[source,java,role="primary"]----@EnableReactiveMethodSecuritypublic 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"]----@EnableReactiveMethodSecurityclass 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"]----@Componentpublic class HelloWorldMessageService {	@PreAuthorize("hasRole('ADMIN')")	public Mono<String> findMessage() {		return Mono.just("Hello World!");	}}----.Kotlin[source,kotlin,role="secondary"]----@Componentclass HelloWorldMessageService {	@PreAuthorize("hasRole('ADMIN')")	fun findMessage(): Mono<String> {		return Mono.just("Hello World!")	}}----====Or, the following class using Kotlin coroutines:====.Kotlin[source,kotlin,role="primary"]----@Componentclass HelloWorldMessageService {    @PreAuthorize("hasRole('ADMIN')")    suspend fun findMessage(): String {        delay(10)        return "Hello World!"    }}----====Combined with our configuration above, `@PreAuthorize("hasRole('ADMIN')")` will ensure that `findByMessage` is only invoked by a user with the role `ADMIN`.It is important to note that any of the expressions in standard method security work for `@EnableReactiveMethodSecurity`.However, at this time we only support return type of `Boolean` or `boolean` of the expression.This means that the expression must not block.When integrating with xref:reactive/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"]----@EnableWebFluxSecurity@EnableReactiveMethodSecuritypublic 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"]----@EnableWebFluxSecurity@EnableReactiveMethodSecurityclass 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]
 |