浏览代码

Add WebFlux to What's New 5.0

Fixes gh-4757
Rob Winch 7 年之前
父节点
当前提交
8e6c726fb2
共有 2 个文件被更改,包括 301 次插入1 次删除
  1. 155 0
      docs/manual/src/docs/asciidoc/_includes/test.adoc
  2. 146 1
      docs/manual/src/docs/asciidoc/index.adoc

+ 155 - 0
docs/manual/src/docs/asciidoc/_includes/test.adoc

@@ -693,3 +693,158 @@ mvc
 	.perform(formLogin().user("admin").roles("USER","ADMIN"))
 	.perform(formLogin().user("admin").roles("USER","ADMIN"))
 	.andExpect(authenticated().withUsername("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")
+	...
+----

+ 146 - 1
docs/manual/src/docs/asciidoc/index.adoc

@@ -384,7 +384,10 @@ Below are the highlights of this milestone release.
 === New Features
 === 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/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]]
 == Samples and Guides (Start Here)
 == 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)
 - 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 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]]
 [[jc-authentication]]
 === 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.
 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
 === 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.
 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.