reactive.adoc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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 `CookieServerCsrfTokenRepository.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 XorServerCsrfTokenRequestAttributeHandler and the
  150. // default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
  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 {
  165. /* Ensures the token is subscribed to. */
  166. }.then(chain.filter(exchange))
  167. }
  168. }
  169. ----
  170. ====
  171. ==== I need to opt out of CSRF BREACH protection for another reason
  172. If CSRF BREACH protection does not work for you for another reason, you can opt out using the following configuration:
  173. .Opt out of `CsrfToken` BREACH protection
  174. ====
  175. .Java
  176. [source,java,role="primary"]
  177. ----
  178. @Bean
  179. SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  180. ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
  181. http
  182. // ...
  183. .csrf((csrf) -> csrf
  184. .csrfTokenRequestHandler(requestHandler)
  185. );
  186. return http.build();
  187. }
  188. ----
  189. .Kotlin
  190. [source,kotlin,role="secondary"]
  191. ----
  192. @Bean
  193. open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  194. val requestHandler = ServerCsrfTokenRequestAttributeHandler()
  195. return http {
  196. // ...
  197. csrf {
  198. csrfTokenRequestHandler = requestHandler
  199. }
  200. }
  201. }
  202. ----
  203. ====
  204. == Use `AuthorizationManager` for Method Security
  205. 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.
  206. Should you run into trouble with making these changes, you can follow the
  207. <<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
  208. 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.
  209. [[reactive-change-to-useauthorizationmanager]]
  210. === Change `useAuthorizationManager` to `true`
  211. To opt in, change `useAuthorizationManager` to `true` like so:
  212. ====
  213. .Java
  214. [source,java,role="primary"]
  215. ----
  216. @EnableReactiveMethodSecurity
  217. ----
  218. .Kotlin
  219. [source,kotlin,role="secondary"]
  220. ----
  221. @EnableReactiveMethodSecurity
  222. ----
  223. ====
  224. changes to:
  225. ====
  226. .Java
  227. [source,java,role="primary"]
  228. ----
  229. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  230. ----
  231. .Kotlin
  232. [source,kotlin,role="secondary"]
  233. ----
  234. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  235. ----
  236. ====
  237. [[reactive-check-for-annotationconfigurationexceptions]]
  238. === Check for ``AnnotationConfigurationException``s
  239. `useAuthorizationManager` activates stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  240. 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.
  241. [[reactive-authorizationmanager-methods-opt-out]]
  242. === Opt-out Steps
  243. If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
  244. ====
  245. .Java
  246. [source,java,role="primary"]
  247. ----
  248. @EnableReactiveMethodSecurity
  249. ----
  250. .Kotlin
  251. [source,kotlin,role="secondary"]
  252. ----
  253. @EnableReactiveMethodSecurity
  254. ----
  255. ====
  256. to:
  257. ====
  258. .Java
  259. [source,java,role="primary"]
  260. ----
  261. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  262. ----
  263. .Kotlin
  264. [source,kotlin,role="secondary"]
  265. ----
  266. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  267. ----
  268. ====
  269. == Propagate ``AuthenticationServiceException``s
  270. {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`].
  271. 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.
  272. === Configure `ServerAuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  273. To prepare for the 6.0 default, `httpBasic` and `oauth2ResourceServer` should be configured to rethrow ``AuthenticationServiceException``s.
  274. For each, construct the appropriate authentication entry point for `httpBasic` and for `oauth2ResourceServer`:
  275. ====
  276. .Java
  277. [source,java,role="primary"]
  278. ----
  279. ServerAuthenticationEntryPoint bearerEntryPoint = new BearerTokenServerAuthenticationEntryPoint();
  280. ServerAuthenticationEntryPoint basicEntryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);
  281. ----
  282. .Kotlin
  283. [source,kotlin,role="secondary"]
  284. ----
  285. val bearerEntryPoint: ServerAuthenticationEntryPoint = BearerTokenServerAuthenticationEntryPoint()
  286. val basicEntryPoint: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
  287. ----
  288. ====
  289. [NOTE]
  290. ====
  291. If you use a custom `AuthenticationEntryPoint` for either or both mechanisms, use that one instead for the remaining steps.
  292. ====
  293. Then, construct and configure a `ServerAuthenticationEntryPointFailureHandler` for each one:
  294. ====
  295. .Java
  296. [source,java,role="primary"]
  297. ----
  298. AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
  299. bearerFailureHandler.setRethrowAuthenticationServiceException(true);
  300. AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
  301. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  302. ----
  303. .Kotlin
  304. [source,kotlin,role="secondary"]
  305. ----
  306. val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
  307. bearerFailureHandler.setRethrowAuthenticationServiceException(true)
  308. val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
  309. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  310. ----
  311. ====
  312. Finally, wire each authentication failure handler into the DSL, like so:
  313. ====
  314. .Java
  315. [source,java,role="primary"]
  316. ----
  317. http
  318. .httpBasic((basic) -> basic.authenticationFailureHandler(basicFailureHandler))
  319. .oauth2ResourceServer((oauth2) -> oauth2.authenticationFailureHandler(bearerFailureHandler))
  320. ----
  321. .Kotlin
  322. [source,kotlin,role="secondary"]
  323. ----
  324. http {
  325. httpBasic {
  326. authenticationFailureHandler = basicFailureHandler
  327. }
  328. oauth2ResourceServer {
  329. authenticationFailureHandler = bearerFailureHandler
  330. }
  331. }
  332. ----
  333. ====
  334. [[reactive-authenticationfailurehandler-opt-out]]
  335. === Opt-out Steps
  336. 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.
  337. [[add-configuration-annotation]]
  338. == Add `@Configuration` annotation
  339. In 6.0, `@Configuration` is removed from `@EnableWebFluxSecurity` and `@EnableReactiveMethodSecurity`.
  340. To prepare for this, wherever you are using one of these annotations, you may need to add `@Configuration`.
  341. For example, `@EnableReactiveMethodSecurity` changes from:
  342. ====
  343. .Java
  344. [source,java,role="primary"]
  345. ----
  346. @EnableReactiveMethodSecurity
  347. public class MyConfiguration {
  348. // ...
  349. }
  350. ----
  351. .Kotlin
  352. [source,java,role="primary"]
  353. ----
  354. @EnableReactiveMethodSecurity
  355. open class MyConfiguration {
  356. // ...
  357. }
  358. ----
  359. ====
  360. to:
  361. ====
  362. .Java
  363. [source,java,role="primary"]
  364. ----
  365. @Configuration
  366. @EnableReactiveMethodSecurity
  367. public class MyConfiguration {
  368. // ...
  369. }
  370. ----
  371. .Kotlin
  372. [source,java,role="primary"]
  373. ----
  374. @Configuration
  375. @EnableReactiveMethodSecurity
  376. open class MyConfiguration {
  377. // ...
  378. }
  379. ----
  380. ====
  381. == Address OAuth2 Client Deprecations
  382. === `ServerOAuth2AuthorizedClientExchangeFilterFunction`
  383. The method `setAccessTokenExpiresSkew(...)` can be replaced with one of:
  384. * `ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  385. * `RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  386. * `JwtBearerReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  387. The method `setClientCredentialsTokenResponseClient(...)` can be replaced with the constructor `ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)`.
  388. [NOTE]
  389. ====
  390. See xref:reactive/oauth2/client/authorization-grants.adoc#oauth2Client-client-creds-grant[Client Credentials] for more information.
  391. ====
  392. === `WebSessionOAuth2ServerAuthorizationRequestRepository`
  393. The method `setAllowMultipleAuthorizationRequests(...)` has no direct replacement.
  394. === `UnAuthenticatedServerOAuth2AuthorizedClientRepository`
  395. The class `UnAuthenticatedServerOAuth2AuthorizedClientRepository` has no direct replacement. Usage of the class can be replaced with `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager`.
  396. == Add `@Configuration` to `@Enable*` annotations
  397. In 6.0, all Spring Security's `@Enable*` annotations had their `@Configuration` removed.
  398. While convenient, it was not consistent with the rest of the Spring projects and most notably Spring Framework's `@Enable*` annotations.
  399. Additionally, the introduction of support for `@Configuration(proxyBeanMethods=false)` in Spring Framework provides another reason to remove `@Configuration` meta-annotation from Spring Security's `@Enable*` annotations and allow users to opt into their preferred configuration mode.
  400. The following annotations had their `@Configuration` removed:
  401. - `@EnableGlobalAuthentication`
  402. - `@EnableGlobalMethodSecurity`
  403. - `@EnableMethodSecurity`
  404. - `@EnableReactiveMethodSecurity`
  405. - `@EnableWebSecurity`
  406. - `@EnableWebFluxSecurity`
  407. For example, if you are using `@EnableWebFluxSecurity`, you will need to change:
  408. ====
  409. .Java
  410. [source,java,role="primary"]
  411. ----
  412. @EnableWebFluxSecurity
  413. public class SecurityConfig {
  414. // ...
  415. }
  416. ----
  417. ====
  418. to:
  419. ====
  420. .Java
  421. [source,java,role="primary"]
  422. ----
  423. @Configuration
  424. @EnableWebFluxSecurity
  425. public class SecurityConfig {
  426. // ...
  427. }
  428. ----
  429. ====
  430. And the same applies to every other annotation listed above.