2018-09-11 22:01:07 -04:00
[[jc-webflux]]
= WebFlux Security
Spring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.
2021-04-21 17:01:26 -04:00
A few sample applications demonstrate the code:
2018-09-11 22:01:07 -04:00
2021-06-21 08:56:05 -04:00
* 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]
2018-09-11 22:01:07 -04:00
== Minimal WebFlux Security Configuration
2021-04-21 17:01:26 -04:00
The following listing shows a minimal WebFlux Security configuration:
2018-09-11 22:01:07 -04:00
2020-04-08 14:57:25 -04:00
.Minimal WebFlux Security Configuration
====
.Java
[source,java,role="primary"]
2018-09-11 22:01:07 -04:00
-----
2019-10-28 16:08:38 -04:00
2018-09-11 22:01:07 -04:00
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
-----
2020-04-08 14:57:25 -04:00
.Kotlin
[source,kotlin,role="secondary"]
-----
@EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
}
-----
====
2021-04-21 17:01:26 -04:00
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.
2018-09-11 22:01:07 -04:00
== Explicit WebFlux Security Configuration
2021-04-21 17:01:26 -04:00
The following page shows an explicit version of the minimal WebFlux Security configuration:
2018-09-11 22:01:07 -04:00
2020-04-08 14:57:25 -04:00
.Explicit WebFlux Security Configuration
====
.Java
[source,java,role="primary"]
2018-09-11 22:01:07 -04:00
-----
2022-07-26 17:46:01 -04:00
@Configuration
2018-09-11 22:01:07 -04:00
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
@Bean
2019-07-24 12:15:32 -04:00
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
2018-09-11 22:01:07 -04:00
http
2020-01-10 07:10:36 -05:00
.authorizeExchange(exchanges -> exchanges
.anyExchange().authenticated()
2019-07-22 09:31:10 -04:00
)
.httpBasic(withDefaults())
.formLogin(withDefaults());
2018-09-11 22:01:07 -04:00
return http.build();
}
}
-----
2020-04-08 14:57:25 -04:00
.Kotlin
[source,kotlin,role="secondary"]
-----
2022-07-26 17:46:01 -04:00
@Configuration
2020-04-08 14:57:25 -04:00
@EnableWebFluxSecurity
class 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 { }
}
}
}
-----
====
2018-09-11 22:01:07 -04:00
This configuration explicitly sets up all the same things as our minimal configuration.
2021-04-21 17:01:26 -04:00
From here, you can more easily make changes to the defaults.
2020-10-27 06:44:40 -04:00
2021-04-21 17:01:26 -04:00
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].
2020-10-29 05:33:35 -04:00
[[jc-webflux-multiple-filter-chains]]
2020-11-02 14:15:19 -05:00
=== Multiple Chains Support
2020-10-29 05:33:35 -04:00
2021-04-21 17:01:26 -04:00
You can configure multiple `SecurityWebFilterChain` instances to separate configuration by `RequestMatcher` instances.
2020-10-29 05:33:35 -04:00
2021-04-21 17:01:26 -04:00
For example, you can isolate configuration for URLs that start with `/api`:
2020-10-29 05:33:35 -04:00
2021-06-24 05:49:13 -04:00
====
.Java
[source,java,role="primary"]
2020-10-29 05:33:35 -04:00
----
2022-07-26 17:46:01 -04:00
@Configuration
2020-11-02 14:15:19 -05:00
@EnableWebFluxSecurity
static class MultiSecurityHttpConfig {
2020-10-29 05:33:35 -04:00
2020-11-02 14:15:19 -05:00
@Order(Ordered.HIGHEST_PRECEDENCE) <1>
@Bean
SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
http
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) <2>
.authorizeExchange((exchanges) -> exchanges
.anyExchange().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); <3>
return http.build();
}
2020-10-29 05:33:35 -04:00
2020-11-02 14:15:19 -05:00
@Bean
SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) { <4>
http
.authorizeExchange((exchanges) -> exchanges
.anyExchange().authenticated()
)
2021-06-17 09:39:33 -04:00
.httpBasic(withDefaults()); <5>
2020-11-02 14:15:19 -05:00
return http.build();
}
@Bean
ReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
PasswordEncodedUser.user(), PasswordEncodedUser.admin());
}
}
2020-10-29 05:33:35 -04:00
----
2021-06-24 05:49:13 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
2022-07-26 17:46:01 -04:00
@Configuration
2021-06-24 05:49:13 -04:00
@EnableWebFluxSecurity
open 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()
)
}
}
----
2020-11-02 14:15:19 -05:00
<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 application
2021-04-21 17:01:26 -04:00
====
2020-11-02 14:15:19 -05:00
2021-04-21 17:01:26 -04:00
Spring Security selects one `SecurityWebFilterChain` `@Bean` for each request.
It matches the requests in order by the `securityMatcher` definition.
2020-10-29 05:33:35 -04:00
2021-04-21 17:01:26 -04:00
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.
2020-10-27 06:44:40 -04:00