Separate Servlet and WebFlux

Fixes: gh-5836
This commit is contained in:
Rob Winch 2018-09-11 21:01:07 -05:00
parent ed9cd478ba
commit 57359058dd
59 changed files with 204 additions and 190 deletions

View File

@ -0,0 +1,7 @@
= Reactive Applications
include::webflux.adoc[leveloffset=+1]
include::method.adoc[leveloffset=+1]
include::webtestclient.adoc[leveloffset=+1]

View File

@ -0,0 +1,103 @@
[[jc-erms]]
= EnableReactiveMethodSecurity
Spring 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`).
This is necessary to integrate with Reactor's `Context`.
====
[source,java]
----
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();
----
with `this::findMessageByUsername` defined as:
[source,java]
----
Mono<String> findMessageByUsername(String username) {
return Mono.just("Hi " + username);
}
----
Below is a minimal method security configuration when using method security in reactive applications.
[source,java]
----
@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);
}
}
----
Consider the following class:
[source,java]
----
@Component
public class HelloWorldMessageService {
@PreAuthorize("hasRole('ADMIN')")
public Mono<String> findMessage() {
return Mono.just("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 <<jc-webflux>>, the Reactor Context is automatically established by Spring Security according to the authenticated user.
[source,java]
----
@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()
.anyExchange().permitAll()
.and()
.httpBasic().and()
.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);
}
}
----
You can find a complete sample in {gh-samples-url}/javaconfig/hellowebflux-method[hellowebflux-method]

View File

@ -0,0 +1,68 @@
[[jc-webflux]]
= WebFlux Security
Spring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.
You can find a few sample applications that demonstrate the code below:
* Hello WebFlux {gh-samples-url}/javaconfig/hellowebflux[hellowebflux]
* Hello WebFlux.Fn {gh-samples-url}/javaconfig/hellowebfluxfn[hellowebfluxfn]
* Hello WebFlux Method {gh-samples-url}/javaconfig/hellowebflux-method[hellowebflux-method]
== Minimal WebFlux Security Configuration
You can find a minimal WebFlux Security configuration below:
[source,java]
-----
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
-----
This configuration provides form and http basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default log in page and a default log out page, sets up security related HTTP headers, CSRF protection, and more.
== Explicit WebFlux Security Configuration
You can find an explicit version of the minimal WebFlux Security configuration below:
[source,java]
-----
@EnableWebFluxSecurity
public 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()
.anyExchange().authenticated()
.and()
.httpBasic().and()
.formLogin();
return http.build();
}
}
-----
This configuration explicitly sets up all the same things as our minimal configuration.
From here you can easily make the changes to the defaults.

View File

@ -1,8 +1,8 @@
[[test-webflux]] [[test-webflux]]
== WebFlux Support = WebFlux Support
[[test-erms]] [[test-erms]]
=== Reactive Method Security == Reactive Method Security
For example, we can test our example from <<jc-erms>> using the same setup and annotations we did in <<test-method>>. For example, we can test our example from <<jc-erms>> using the same setup and annotations we did in <<test-method>>.
Here is a minimal sample of what we can do: Here is a minimal sample of what we can do:
@ -41,7 +41,7 @@ public class HelloWorldMessageServiceTests {
---- ----
[[test-webtestclient]] [[test-webtestclient]]
=== WebTestClientSupport == WebTestClientSupport
Spring Security provides integration with `WebTestClient`. Spring Security provides integration with `WebTestClient`.
The basic setup looks like this: The basic setup looks like this:
@ -70,7 +70,7 @@ public class HelloWebfluxMethodApplicationTests {
} }
---- ----
==== Authentication === Authentication
After applying the Spring Security support to `WebTestClient` we can use either annotations or `mutateWith` support. After applying the Spring Security support to `WebTestClient` we can use either annotations or `mutateWith` support.
For example: For example:
@ -134,7 +134,7 @@ public void messageWhenMutateWithMockAdminThenOk() throws Exception {
---- ----
==== CSRF Support === CSRF Support
Spring Security also provides support for CSRF testing with `WebTestClient`. Spring Security also provides support for CSRF testing with `WebTestClient`.
For example: For example:

View File

@ -0,0 +1,17 @@
= Servlet Applications
include::preface/index.adoc[leveloffset=+1]
include::architecture/index.adoc[leveloffset=+1]
include::test/index.adoc[leveloffset=+1]
include::web/index.adoc[leveloffset=+1]
include::authorization/index.adoc[leveloffset=+1]
include::additional-topics/index.adoc[leveloffset=+1]
include::data/index.adoc[leveloffset=+1]
include::appendix/index.adoc[leveloffset=+1]

View File

@ -383,74 +383,6 @@ If not configured a status code 200 will be returned by default.
- Section <<cas-singlelogout, Single Logout>> (CAS protocol) - Section <<cas-singlelogout, Single Logout>> (CAS protocol)
- Documentation for the <<nsa-logout, logout element>> in the Spring Security XML Namespace section - Documentation for the <<nsa-logout, logout element>> in the Spring Security XML Namespace section
[[jc-webflux]]
=== WebFlux Security
Spring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.
You can find a few sample applications that demonstrate the code below:
* Hello WebFlux {gh-samples-url}/javaconfig/hellowebflux[hellowebflux]
* Hello WebFlux.Fn {gh-samples-url}/javaconfig/hellowebfluxfn[hellowebfluxfn]
* Hello WebFlux Method {gh-samples-url}/javaconfig/hellowebflux-method[hellowebflux-method]
==== Minimal WebFlux Security Configuration
You can find a minimal WebFlux Security configuration below:
[source,java]
-----
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
-----
This configuration provides form and http basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default log in page and a default log out page, sets up security related HTTP headers, CSRF protection, and more.
==== Explicit WebFlux Security Configuration
You can find an explicit version of the minimal WebFlux Security configuration below:
[source,java]
-----
@EnableWebFluxSecurity
public 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()
.anyExchange().authenticated()
.and()
.httpBasic().and()
.formLogin();
return http.build();
}
}
-----
This configuration explicitly sets up all the same things as our minimal configuration.
From here you can easily make the changes to the defaults.
[[jc-oauth2login]] [[jc-oauth2login]]
=== OAuth 2.0 Login === OAuth 2.0 Login
@ -1302,110 +1234,6 @@ public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc. For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc.
[[jc-erms]]
==== EnableReactiveMethodSecurity
Spring 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`).
This is necessary to integrate with Reactor's `Context`.
====
[source,java]
----
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();
----
with `this::findMessageByUsername` defined as:
[source,java]
----
Mono<String> findMessageByUsername(String username) {
return Mono.just("Hi " + username);
}
----
Below is a minimal method security configuration when using method security in reactive applications.
[source,java]
----
@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);
}
}
----
Consider the following class:
[source,java]
----
@Component
public class HelloWorldMessageService {
@PreAuthorize("hasRole('ADMIN')")
public Mono<String> findMessage() {
return Mono.just("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 <<jc-webflux>>, the Reactor Context is automatically established by Spring Security according to the authenticated user.
[source,java]
----
@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()
.anyExchange().permitAll()
.and()
.httpBasic().and()
.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);
}
}
----
You can find a complete sample in {gh-samples-url}/javaconfig/hellowebflux-method[hellowebflux-method]
=== Post Processing Configured Objects === Post Processing Configured Objects
Spring Security's Java Configuration does not expose every property of every object that it configures. Spring Security's Java Configuration does not expose every property of every object that it configures.

View File

@ -2,24 +2,15 @@
Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant
:include-dir: _includes :include-dir: _includes
:security-api-url: http://docs.spring.io/spring-security/site/docs/current/apidocs/ :security-api-url: http://docs.spring.io/spring-security/site/docs/current/apidocs/
:source-indent: 0
:tabsize: 2
Spring Security is a powerful and highly customizable authentication and access-control framework. Spring Security is a powerful and highly customizable authentication and access-control framework.
It is the de-facto standard for securing Spring-based applications. It is the de-facto standard for securing Spring-based applications.
include::{include-dir}/preface/index.adoc[]
include::{include-dir}/architecture/index.adoc[] include::{include-dir}/servlet/index.adoc[]
include::{include-dir}/test/index.adoc[] include::{include-dir}/reactive/index.adoc[]
include::{include-dir}/web/index.adoc[]
include::{include-dir}/authorization/index.adoc[]
include::{include-dir}/additional-topics/index.adoc[]
include::{include-dir}/data/index.adoc[]
include::{include-dir}/appendix/index.adoc[]