reactive.adoc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. = Reactive Migrations
  2. If you have already performed the xref:migration/index.adoc[initial migration steps] for your Reactive application, you're now ready to perform steps specific to Reactive applications.
  3. == Exploit Protection Migrations
  4. The following steps relate to changes around how to configure CSRF.
  5. === Configure `tokenFromMultipartDataEnabled`
  6. In Spring Security 5.8, the method `tokenFromMultipartDataEnabled` was deprecated in favor of `ServerCsrfTokenRequestAttributeHandler#setTokenFromMultipartDataEnabled`.
  7. To address the deprecation, the following code:
  8. .Configure `tokenFromMultipartDataEnabled` with DSL
  9. ====
  10. .Java
  11. [source,java,role="primary"]
  12. ----
  13. @Bean
  14. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  15. http
  16. // ...
  17. .csrf((csrf) -> csrf
  18. .tokenFromMultipartDataEnabled(true)
  19. );
  20. return http.build();
  21. }
  22. ----
  23. .Kotlin
  24. [source,kotlin,role="secondary"]
  25. ----
  26. @Bean
  27. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  28. return http {
  29. // ...
  30. csrf {
  31. tokenFromMultipartDataEnabled = true
  32. }
  33. }
  34. }
  35. ----
  36. ====
  37. can be replaced with:
  38. .Configure `tokenFromMultipartDataEnabled` with `ServerCsrfTokenRequestAttributeHandler`
  39. ====
  40. .Java
  41. [source,java,role="primary"]
  42. ----
  43. @Bean
  44. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  45. ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
  46. requestHandler.setTokenFromMultipartDataEnabled(true);
  47. http
  48. // ...
  49. .csrf((csrf) -> csrf
  50. .csrfTokenRequestHandler(requestHandler)
  51. );
  52. return http.build();
  53. }
  54. ----
  55. .Kotlin
  56. [source,kotlin,role="secondary"]
  57. ----
  58. @Bean
  59. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  60. val requestHandler = ServerCsrfTokenRequestAttributeHandler()
  61. requestHandler.tokenFromMultipartDataEnabled = true
  62. return http {
  63. // ...
  64. csrf {
  65. csrfTokenRequestHandler = requestHandler
  66. }
  67. }
  68. }
  69. ----
  70. ====
  71. === Protect against CSRF BREACH
  72. You can opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
  73. .`CsrfToken` BREACH Protection
  74. ====
  75. .Java
  76. [source,java,role="primary"]
  77. ----
  78. @Bean
  79. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  80. XorServerCsrfTokenRequestAttributeHandler requestHandler = new XorServerCsrfTokenRequestAttributeHandler();
  81. // ...
  82. http
  83. // ...
  84. .csrf((csrf) -> csrf
  85. .csrfTokenRequestHandler(requestHandler)
  86. );
  87. return http.build();
  88. }
  89. ----
  90. .Kotlin
  91. [source,kotlin,role="secondary"]
  92. ----
  93. @Bean
  94. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  95. val requestHandler = XorServerCsrfTokenRequestAttributeHandler()
  96. // ...
  97. return http {
  98. // ...
  99. csrf {
  100. csrfTokenRequestHandler = requestHandler
  101. }
  102. }
  103. }
  104. ----
  105. ====
  106. [[reactive-csrf-breach-opt-out]]
  107. === Opt-out Steps
  108. If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
  109. ==== I am using AngularJS or another Javascript framework
  110. If you are using AngularJS and the https://angular.io/api/common/http/HttpClientXsrfModule[HttpClientXsrfModule] (or a similar module in another framework) along with `CookieCsrfTokenRepository.withHttpOnlyFalse()`, you may find that automatic support no longer works.
  111. In this case, you can configure Spring Security to validate the raw `CsrfToken` from the cookie while keeping CSRF BREACH protection of the response using a custom `ServerCsrfTokenRequestHandler` with delegation, like so:
  112. .Configure `CsrfToken` BREACH Protection to validate raw tokens
  113. ====
  114. .Java
  115. [source,java,role="primary"]
  116. ----
  117. @Bean
  118. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  119. CookieServerCsrfTokenRepository tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse();
  120. XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
  121. // Use only the handle() method of XorServerCsrfTokenRequestAttributeHandler and the
  122. // default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
  123. ServerCsrfTokenRequestHandler requestHandler = delegate::handle;
  124. http
  125. // ...
  126. .csrf((csrf) -> csrf
  127. .csrfTokenRepository(tokenRepository)
  128. .csrfTokenRequestHandler(requestHandler)
  129. );
  130. return http.build();
  131. }
  132. @Bean
  133. WebFilter csrfCookieWebFilter() {
  134. return (exchange, chain) -> {
  135. Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
  136. return csrfToken.doOnSuccess(token -> {
  137. /* Ensures the token is subscribed to. */
  138. }).then(chain.filter(exchange));
  139. };
  140. }
  141. ----
  142. .Kotlin
  143. [source,kotlin,role="secondary"]
  144. ----
  145. @Bean
  146. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  147. val tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
  148. val delegate = XorServerCsrfTokenRequestAttributeHandler()
  149. // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
  150. // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
  151. val requestHandler = ServerCsrfTokenRequestHandler(delegate::handle)
  152. return http.invoke {
  153. // ...
  154. csrf {
  155. csrfTokenRepository = tokenRepository
  156. csrfTokenRequestHandler = requestHandler
  157. }
  158. }
  159. }
  160. @Bean
  161. fun csrfCookieWebFilter(): WebFilter {
  162. return WebFilter { exchange, chain ->
  163. val csrfToken = exchange.getAttribute<Mono<CsrfToken>>(CsrfToken::class.java.name) ?: Mono.empty()
  164. csrfToken.doOnSuccess { }.then(chain.filter(exchange))
  165. }
  166. }
  167. ----
  168. ====
  169. ==== I need to opt out of CSRF BREACH protection for another reason
  170. If CSRF BREACH protection does not work for you for another reason, you can opt out using the following configuration:
  171. .Opt out of `CsrfToken` BREACH protection
  172. ====
  173. .Java
  174. [source,java,role="primary"]
  175. ----
  176. @Bean
  177. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  178. ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
  179. http
  180. // ...
  181. .csrf((csrf) -> csrf
  182. .csrfTokenRequestHandler(requestHandler)
  183. );
  184. return http.build();
  185. }
  186. ----
  187. .Kotlin
  188. [source,kotlin,role="secondary"]
  189. ----
  190. @Bean
  191. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  192. val requestHandler = ServerCsrfTokenRequestAttributeHandler()
  193. return http {
  194. // ...
  195. csrf {
  196. csrfTokenRequestHandler = requestHandler
  197. }
  198. }
  199. }
  200. ----
  201. ====
  202. == Use `AuthorizationManager` for Method Security
  203. xref:reactive/authorization/method.adoc[Method Security] has been xref:reactive/authorization/method.adoc#jc-enable-reactive-method-security-authorization-manager[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
  204. Should you run into trouble with making these changes, you can follow the
  205. <<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
  206. In Spring Security 5.8, `useAuthorizationManager` was added to {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.html[`@EnableReactiveMethodSecurity`] to allow applications to opt in to ``AuthorizationManager``'s features.
  207. [[reactive-change-to-useauthorizationmanager]]
  208. === Change `useAuthorizationManager` to `true`
  209. To opt in, change `useAuthorizationManager` to `true` like so:
  210. ====
  211. .Java
  212. [source,java,role="primary"]
  213. ----
  214. @EnableReactiveMethodSecurity
  215. ----
  216. .Kotlin
  217. [source,kotlin,role="secondary"]
  218. ----
  219. @EnableReactiveMethodSecurity
  220. ----
  221. ====
  222. changes to:
  223. ====
  224. .Java
  225. [source,java,role="primary"]
  226. ----
  227. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  228. ----
  229. .Kotlin
  230. [source,kotlin,role="secondary"]
  231. ----
  232. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  233. ----
  234. ====
  235. [[reactive-check-for-annotationconfigurationexceptions]]
  236. === Check for ``AnnotationConfigurationException``s
  237. `useAuthorizationManager` activates stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  238. If after turning on `useAuthorizationManager` you see ``AnnotationConfigurationException``s in your logs, follow the instructions in the exception message to clean up your application's method security annotation usage.
  239. [[reactive-authorizationmanager-methods-opt-out]]
  240. === Opt-out Steps
  241. If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
  242. ====
  243. .Java
  244. [source,java,role="primary"]
  245. ----
  246. @EnableReactiveMethodSecurity
  247. ----
  248. .Kotlin
  249. [source,kotlin,role="secondary"]
  250. ----
  251. @EnableReactiveMethodSecurity
  252. ----
  253. ====
  254. to:
  255. ====
  256. .Java
  257. [source,java,role="primary"]
  258. ----
  259. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  260. ----
  261. .Kotlin
  262. [source,kotlin,role="secondary"]
  263. ----
  264. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  265. ----
  266. ====
  267. == Propagate ``AuthenticationServiceException``s
  268. {security-api-url}org/springframework/security/web/server/Webauthentication/AuthenticationWebFilter.html[`AuthenticationFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/web/server/ServerAuthenticationEntryPoint.html[`ServerAuthenticationEntryPoint`].
  269. Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container.
  270. === Configure `ServerAuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  271. To prepare for the 6.0 default, `httpBasic` and `oauth2ResourceServer` should be configured to rethrow ``AuthenticationServiceException``s.
  272. For each, construct the appropriate authentication entry point for `httpBasic` and for `oauth2ResourceServer`:
  273. ====
  274. .Java
  275. [source,java,role="primary"]
  276. ----
  277. ServerAuthenticationEntryPoint bearerEntryPoint = new BearerTokenServerAuthenticationEntryPoint();
  278. ServerAuthenticationEntryPoint basicEntryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);
  279. ----
  280. .Kotlin
  281. [source,kotlin,role="secondary"]
  282. ----
  283. val bearerEntryPoint: ServerAuthenticationEntryPoint = BearerTokenServerAuthenticationEntryPoint()
  284. val basicEntryPoint: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
  285. ----
  286. ====
  287. [NOTE]
  288. ====
  289. If you use a custom `AuthenticationEntryPoint` for either or both mechanisms, use that one instead for the remaining steps.
  290. ====
  291. Then, construct and configure a `ServerAuthenticationEntryPointFailureHandler` for each one:
  292. ====
  293. .Java
  294. [source,java,role="primary"]
  295. ----
  296. AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
  297. bearerFailureHandler.setRethrowAuthenticationServiceException(true);
  298. AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
  299. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  300. ----
  301. .Kotlin
  302. [source,kotlin,role="secondary"]
  303. ----
  304. val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
  305. bearerFailureHandler.setRethrowAuthenticationServiceException(true)
  306. val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
  307. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  308. ----
  309. ====
  310. Finally, wire each authentication failure handler into the DSL, like so:
  311. ====
  312. .Java
  313. [source,java,role="primary"]
  314. ----
  315. http
  316. .httpBasic((basic) -> basic.authenticationFailureHandler(basicFailureHandler))
  317. .oauth2ResourceServer((oauth2) -> oauth2.authenticationFailureHandler(bearerFailureHandler))
  318. ----
  319. .Kotlin
  320. [source,kotlin,role="secondary"]
  321. ----
  322. http {
  323. httpBasic {
  324. authenticationFailureHandler = basicFailureHandler
  325. }
  326. oauth2ResourceServer {
  327. authenticationFailureHandler = bearerFailureHandler
  328. }
  329. }
  330. ----
  331. ====
  332. [[reactive-authenticationfailurehandler-opt-out]]
  333. === Opt-out Steps
  334. To opt-out of the 6.0 defaults and instead continue to pass `AuthenticationServiceException` on to ``ServerAuthenticationEntryPoint``s, you can follow the same steps as above, except set `rethrowAuthenticationServiceException` to false.
  335. == Address OAuth2 Client Deprecations
  336. === `ServerOAuth2AuthorizedClientExchangeFilterFunction`
  337. The method `setAccessTokenExpiresSkew(...)` can be replaced with one of:
  338. * `ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  339. * `RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  340. * `JwtBearerReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  341. The method `setClientCredentialsTokenResponseClient(...)` can be replaced with the constructor `ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)`.
  342. [NOTE]
  343. ====
  344. See xref:reactive/oauth2/client/authorization-grants.adoc#oauth2Client-client-creds-grant[Client Credentials] for more information.
  345. ====
  346. === `WebSessionOAuth2ServerAuthorizationRequestRepository`
  347. The method `setAllowMultipleAuthorizationRequests(...)` has no direct replacement.
  348. === `UnAuthenticatedServerOAuth2AuthorizedClientRepository`
  349. The class `UnAuthenticatedServerOAuth2AuthorizedClientRepository` has no direct replacement. Usage of the class can be replaced with `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager`.