| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 | [[jc-webflux]]= WebFlux SecuritySpring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.A few sample applications demonstrate the code:* Hello WebFlux {gh-samples-url}/reactive/webflux/java/hello-security[hellowebflux]* Hello WebFlux.Fn {gh-samples-url}/reactive/webflux-fn/hello-security[hellowebfluxfn]* Hello WebFlux Method {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method]== Minimal WebFlux Security ConfigurationThe following listing shows a minimal WebFlux Security configuration:.Minimal WebFlux Security Configuration[tabs]======Java::+[source,java,role="primary"]-----@Configuration@EnableWebFluxSecuritypublic class HelloWebfluxSecurityConfig {	@Bean	public MapReactiveUserDetailsService userDetailsService() {		UserDetails user = User.withDefaultPasswordEncoder()			.username("user")			.password("user")			.roles("USER")			.build();		return new MapReactiveUserDetailsService(user);	}}-----Kotlin::+[source,kotlin,role="secondary"]-----@Configuration@EnableWebFluxSecurityclass HelloWebfluxSecurityConfig {    @Bean    fun userDetailsService(): ReactiveUserDetailsService {        val userDetails = User.withDefaultPasswordEncoder()                .username("user")                .password("user")                .roles("USER")                .build()        return MapReactiveUserDetailsService(userDetails)    }}-----======This configuration provides form and HTTP basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default login page and a default logout page, sets up security related HTTP headers, adds CSRF protection, and more.== Explicit WebFlux Security ConfigurationThe following page shows an explicit version of the minimal WebFlux Security configuration:.Explicit WebFlux Security Configuration[tabs]======Java::+[source,java,role="primary"]-----@Configuration@EnableWebFluxSecuritypublic class HelloWebfluxSecurityConfig {	@Bean	public MapReactiveUserDetailsService userDetailsService() {		UserDetails user = User.withDefaultPasswordEncoder()			.username("user")			.password("user")			.roles("USER")			.build();		return new MapReactiveUserDetailsService(user);	}	@Bean	public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {		http			.authorizeExchange((authorize) -> authorize			    .anyExchange().authenticated()			)			.httpBasic(withDefaults())			.formLogin(withDefaults());		return http.build();	}}-----Kotlin::+[source,kotlin,role="secondary"]-----import org.springframework.security.config.web.server.invoke@Configuration@EnableWebFluxSecurityclass HelloWebfluxSecurityConfig {    @Bean    fun userDetailsService(): ReactiveUserDetailsService {        val userDetails = User.withDefaultPasswordEncoder()                .username("user")                .password("user")                .roles("USER")                .build()        return MapReactiveUserDetailsService(userDetails)    }    @Bean    fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {        return http {            authorizeExchange {                authorize(anyExchange, authenticated)            }            formLogin { }            httpBasic { }        }    }}-----======[NOTE]Make sure to import the `org.springframework.security.config.web.server.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.This configuration explicitly sets up all the same things as our minimal configuration.From here, you can more easily make changes to the defaults.You can find more examples of explicit configuration in unit tests, by searching for https://github.com/spring-projects/spring-security/search?q=path%3Aconfig%2Fsrc%2Ftest%2F+EnableWebFluxSecurity[`EnableWebFluxSecurity` in the `config/src/test/` directory].[[jc-webflux-multiple-filter-chains]]=== Multiple Chains SupportYou can configure multiple `SecurityWebFilterChain` instances to separate configuration by `RequestMatcher` instances.For example, you can isolate configuration for URLs that start with `/api`:[tabs]======Java::+[source,java,role="primary"]----@Configuration@EnableWebFluxSecuritystatic class MultiSecurityHttpConfig {    @Order(Ordered.HIGHEST_PRECEDENCE)                                                      <1>    @Bean    SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {        http            .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**"))      <2>            .authorizeExchange((authorize) -> authorize                .anyExchange().authenticated()            )            .oauth2ResourceServer(OAuth2ResourceServerSpec::jwt);                           <3>        return http.build();    }    @Bean    SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) {                       <4>        http            .authorizeExchange((authorize) -> authorize                .anyExchange().authenticated()            )            .httpBasic(withDefaults());                                                     <5>        return http.build();    }    @Bean    ReactiveUserDetailsService userDetailsService() {        return new MapReactiveUserDetailsService(                PasswordEncodedUser.user(), PasswordEncodedUser.admin());    }}----Kotlin::+[source,kotlin,role="secondary"]----import org.springframework.security.config.web.server.invoke@Configuration@EnableWebFluxSecurityopen class MultiSecurityHttpConfig {    @Order(Ordered.HIGHEST_PRECEDENCE)                                                      <1>    @Bean    open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {        return http {            securityMatcher(PathPatternParserServerWebExchangeMatcher("/api/**"))           <2>            authorizeExchange {                authorize(anyExchange, authenticated)            }            oauth2ResourceServer {                jwt { }                                                                     <3>            }        }    }    @Bean    open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {            <4>        return http {            authorizeExchange {                authorize(anyExchange, authenticated)            }            httpBasic { }                                                                   <5>        }    }    @Bean    open fun userDetailsService(): ReactiveUserDetailsService {        return MapReactiveUserDetailsService(            PasswordEncodedUser.user(), PasswordEncodedUser.admin()        )    }}----======<1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first<2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`<3> Specify the authentication mechanisms that will be used for `/api/**` endpoints<4> Create another instance of `SecurityWebFilterChain` with lower precedence to match all other URLs<5> Specify the authentication mechanisms that will be used for the rest of the applicationSpring Security selects one `SecurityWebFilterChain` `@Bean` for each request.It matches the requests in order by the `securityMatcher` definition.In this case, that means that, if the URL path starts with `/api`, Spring Security uses `apiHttpSecurity`.If the URL does not start with `/api`, Spring Security defaults to `webHttpSecurity`, which has an implied `securityMatcher` that matches any request.
 |