webflux.adoc 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. [[jc-webflux]]
  2. = WebFlux Security
  3. Spring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.
  4. A few sample applications demonstrate the code:
  5. * Hello WebFlux {gh-samples-url}/reactive/webflux/java/hello-security[hellowebflux]
  6. * Hello WebFlux.Fn {gh-samples-url}/reactive/webflux-fn/hello-security[hellowebfluxfn]
  7. * Hello WebFlux Method {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method]
  8. == Minimal WebFlux Security Configuration
  9. The following listing shows a minimal WebFlux Security configuration:
  10. .Minimal WebFlux Security Configuration
  11. ====
  12. .Java
  13. [source,java,role="primary"]
  14. -----
  15. @Configuration
  16. @EnableWebFluxSecurity
  17. public class HelloWebfluxSecurityConfig {
  18. @Bean
  19. public MapReactiveUserDetailsService userDetailsService() {
  20. UserDetails user = User.withDefaultPasswordEncoder()
  21. .username("user")
  22. .password("user")
  23. .roles("USER")
  24. .build();
  25. return new MapReactiveUserDetailsService(user);
  26. }
  27. }
  28. -----
  29. .Kotlin
  30. [source,kotlin,role="secondary"]
  31. -----
  32. @Configuration
  33. @EnableWebFluxSecurity
  34. class HelloWebfluxSecurityConfig {
  35. @Bean
  36. fun userDetailsService(): ReactiveUserDetailsService {
  37. val userDetails = User.withDefaultPasswordEncoder()
  38. .username("user")
  39. .password("user")
  40. .roles("USER")
  41. .build()
  42. return MapReactiveUserDetailsService(userDetails)
  43. }
  44. }
  45. -----
  46. ====
  47. 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.
  48. == Explicit WebFlux Security Configuration
  49. The following page shows an explicit version of the minimal WebFlux Security configuration:
  50. .Explicit WebFlux Security Configuration
  51. ====
  52. .Java
  53. [source,java,role="primary"]
  54. -----
  55. @Configuration
  56. @EnableWebFluxSecurity
  57. public class HelloWebfluxSecurityConfig {
  58. @Bean
  59. public MapReactiveUserDetailsService userDetailsService() {
  60. UserDetails user = User.withDefaultPasswordEncoder()
  61. .username("user")
  62. .password("user")
  63. .roles("USER")
  64. .build();
  65. return new MapReactiveUserDetailsService(user);
  66. }
  67. @Bean
  68. public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
  69. http
  70. .authorizeExchange(exchanges -> exchanges
  71. .anyExchange().authenticated()
  72. )
  73. .httpBasic(withDefaults())
  74. .formLogin(withDefaults());
  75. return http.build();
  76. }
  77. }
  78. -----
  79. .Kotlin
  80. [source,kotlin,role="secondary"]
  81. -----
  82. @Configuration
  83. @EnableWebFluxSecurity
  84. class HelloWebfluxSecurityConfig {
  85. @Bean
  86. fun userDetailsService(): ReactiveUserDetailsService {
  87. val userDetails = User.withDefaultPasswordEncoder()
  88. .username("user")
  89. .password("user")
  90. .roles("USER")
  91. .build()
  92. return MapReactiveUserDetailsService(userDetails)
  93. }
  94. @Bean
  95. fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  96. return http {
  97. authorizeExchange {
  98. authorize(anyExchange, authenticated)
  99. }
  100. formLogin { }
  101. httpBasic { }
  102. }
  103. }
  104. }
  105. -----
  106. ====
  107. This configuration explicitly sets up all the same things as our minimal configuration.
  108. From here, you can more easily make changes to the defaults.
  109. 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].
  110. [[jc-webflux-multiple-filter-chains]]
  111. === Multiple Chains Support
  112. You can configure multiple `SecurityWebFilterChain` instances to separate configuration by `RequestMatcher` instances.
  113. For example, you can isolate configuration for URLs that start with `/api`:
  114. ====
  115. .Java
  116. [source,java,role="primary"]
  117. ----
  118. @Configuration
  119. @EnableWebFluxSecurity
  120. static class MultiSecurityHttpConfig {
  121. @Order(Ordered.HIGHEST_PRECEDENCE) <1>
  122. @Bean
  123. SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
  124. http
  125. .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) <2>
  126. .authorizeExchange((exchanges) -> exchanges
  127. .anyExchange().authenticated()
  128. )
  129. .oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); <3>
  130. return http.build();
  131. }
  132. @Bean
  133. SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) { <4>
  134. http
  135. .authorizeExchange((exchanges) -> exchanges
  136. .anyExchange().authenticated()
  137. )
  138. .httpBasic(withDefaults()); <5>
  139. return http.build();
  140. }
  141. @Bean
  142. ReactiveUserDetailsService userDetailsService() {
  143. return new MapReactiveUserDetailsService(
  144. PasswordEncodedUser.user(), PasswordEncodedUser.admin());
  145. }
  146. }
  147. ----
  148. .Kotlin
  149. [source,kotlin,role="secondary"]
  150. ----
  151. @Configuration
  152. @EnableWebFluxSecurity
  153. open class MultiSecurityHttpConfig {
  154. @Order(Ordered.HIGHEST_PRECEDENCE) <1>
  155. @Bean
  156. open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
  157. return http {
  158. securityMatcher(PathPatternParserServerWebExchangeMatcher("/api/**")) <2>
  159. authorizeExchange {
  160. authorize(anyExchange, authenticated)
  161. }
  162. oauth2ResourceServer {
  163. jwt { } <3>
  164. }
  165. }
  166. }
  167. @Bean
  168. open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain { <4>
  169. return http {
  170. authorizeExchange {
  171. authorize(anyExchange, authenticated)
  172. }
  173. httpBasic { } <5>
  174. }
  175. }
  176. @Bean
  177. open fun userDetailsService(): ReactiveUserDetailsService {
  178. return MapReactiveUserDetailsService(
  179. PasswordEncodedUser.user(), PasswordEncodedUser.admin()
  180. )
  181. }
  182. }
  183. ----
  184. <1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first
  185. <2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`
  186. <3> Specify the authentication mechanisms that will be used for `/api/**` endpoints
  187. <4> Create another instance of `SecurityWebFilterChain` with lower precedence to match all other URLs
  188. <5> Specify the authentication mechanisms that will be used for the rest of the application
  189. ====
  190. Spring Security selects one `SecurityWebFilterChain` `@Bean` for each request.
  191. It matches the requests in order by the `securityMatcher` definition.
  192. In this case, that means that, if the URL path starts with `/api`, Spring Security uses `apiHttpSecurity`.
  193. If the URL does not start with `/api`, Spring Security defaults to `webHttpSecurity`, which has an implied `securityMatcher` that matches any request.