Add WebFlux to What's New 5.0

Fixes gh-4757
This commit is contained in:
Rob Winch 2017-10-30 14:58:33 -05:00
parent d435f149eb
commit 8e6c726fb2
2 changed files with 301 additions and 1 deletions

View File

@ -693,3 +693,158 @@ mvc
.perform(formLogin().user("admin").roles("USER","ADMIN"))
.andExpect(authenticated().withUsername("admin"));
----
[[test-webflux]]
== WebFlux Support
Spring Security provides test integration with Spring WebFlux for both method security and WebFlux.
You can find a complete working sample at {gh-samples-url}/javaconfig/hellowebflux-method[hellowebflux-method]
[[test-erms]]
=== Reactive Method Security
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:
[source,java]
----
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)
public class HelloWorldMessageServiceTests {
@Autowired
HelloWorldMessageService messages;
@Test
public void messagesWhenNotAuthenticatedThenDenied() {
StepVerifier.create(this.messages.findMessage())
.expectError(AccessDeniedException.class)
.verify();
}
@Test
@WithMockUser
public void messagesWhenUserThenDenied() {
StepVerifier.create(this.messages.findMessage())
.expectError(AccessDeniedException.class)
.verify();
}
@Test
@WithMockUser(roles = "ADMIN")
public void messagesWhenAdminThenOk() {
StepVerifier.create(this.messages.findMessage())
.expectNext("Hello World!")
.verifyComplete();
}
}
----
[[test-webtestclient]]
=== WebTestClientSupport
Spring Security provides integration with `WebTestClient`.
The basic setup looks like this:
[source,java]
----
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)
public class HelloWebfluxMethodApplicationTests {
@Autowired
ApplicationContext context;
WebTestClient rest;
@Before
public void setup() {
this.rest = WebTestClient
.bindToApplicationContext(this.context)
// add Spring Security test Support
.apply(springSecurity())
.configureClient()
.filter(basicAuthentication())
.build();
}
// ...
}
----
==== Authentication
After applying the Spring Security support to `WebTestClient` we can use either annotations or `mutateWith` support.
For example:
[source,java]
----
@Test
public void messageWhenNotAuthenticated() throws Exception {
this.rest
.get()
.uri("/message")
.exchange()
.expectStatus().isUnauthorized();
}
// --- WithMockUser ---
@Test
@WithMockUser
public void messageWhenWithMockUserThenForbidden() throws Exception {
this.rest
.get()
.uri("/message")
.exchange()
.expectStatus().isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
@WithMockUser(roles = "ADMIN")
public void messageWhenWithMockAdminThenOk() throws Exception {
this.rest
.get()
.uri("/message")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World!");
}
// --- mutateWith mockUser ---
@Test
public void messageWhenMutateWithMockUserThenForbidden() throws Exception {
this.rest
.mutateWith(mockUser())
.get()
.uri("/message")
.exchange()
.expectStatus().isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
public void messageWhenMutateWithMockAdminThenOk() throws Exception {
this.rest
.mutateWith(mockUser().roles("ADMIN"))
.get()
.uri("/message")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World!");
}
----
==== CSRF Support
Spring Security also provides support for CSRF testing with `WebTestClient`.
For example:
[source,java]
----
this.rest
// provide a valid CSRF token
.mutateWith(csrf())
.post()
.uri("/login")
...
----

View File

@ -384,7 +384,10 @@ Below are the highlights of this milestone release.
=== New Features
* https://github.com/spring-projects/spring-security/issues/3907[#3907] - Support added for OAuth 2.0 Login (start with {gh-samples-url}/boot/oauth2login/README.adoc[Sample README])
* https://github.com/spring-projects/spring-security/issues/4128[#4128] - Initial Reactive Support (start with {gh-samples-url}/javaconfig/hellowebflux[hellowebflux])
* Reactive Support
** <<jc-webflux,@EnableWebFluxSecurity>>
** <<jc-erms,@EnableReactiveMethodSecurity>>
** <<test-webflux,WebFlux Testing Support>>
[[samples]]
== Samples and Guides (Start Here)
@ -763,6 +766,77 @@ a status code 200 will be returned by default.
- Section <<cas-singlelogout, Single Logout>> (CAS protocol)
- 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 userDetailsRepository() {
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 userDetailsRepository() {
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 sames things as our minimal configuration.
From here you can easily make the changes to the defaults.
[[jc-authentication]]
=== Authentication
@ -1059,6 +1133,77 @@ public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
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].
Below is a minimal method security configuration when using method security in reactive applications.
[source,java]
----
@EnableReactiveMethodSecurity
public class SecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsRepository() {
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 userDetailsRepository() {
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
Spring Security's Java Configuration does not expose every property of every object that it configures. This simplifies the configuration for a majority of users. Afterall, if every property was exposed, users could use standard bean configuration.