migration.adoc 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. [[migration]]
  2. = Migrating to 6.0
  3. The Spring Security team has prepared the 5.8 release to simplify upgrading to Spring Security 6.0.
  4. Use 5.8 and the steps below to minimize changes when
  5. ifdef::spring-security-version[]
  6. xref:6.0.0@migration.adoc[updating to 6.0]
  7. endif::[]
  8. ifndef::spring-security-version[]
  9. updating to 6.0
  10. endif::[]
  11. .
  12. == Servlet
  13. === Defer Loading CsrfToken
  14. In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
  15. This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
  16. In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
  17. To opt into the new Spring Security 6 default, the following configuration can be used.
  18. .Defer Loading `CsrfToken`
  19. ====
  20. .Java
  21. [source,java,role="primary"]
  22. ----
  23. @Bean
  24. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  25. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  26. // set the name of the attribute the CsrfToken will be populated on
  27. requestHandler.setCsrfRequestAttributeName("_csrf");
  28. http
  29. // ...
  30. .csrf((csrf) -> csrf
  31. .csrfTokenRequestHandler(requestHandler)
  32. );
  33. return http.build();
  34. }
  35. ----
  36. .Kotlin
  37. [source,kotlin,role="secondary"]
  38. ----
  39. @Bean
  40. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  41. val requestHandler = CsrfTokenRequestAttributeHandler()
  42. // set the name of the attribute the CsrfToken will be populated on
  43. requestHandler.setCsrfRequestAttributeName("_csrf")
  44. http {
  45. csrf {
  46. csrfTokenRequestHandler = requestHandler
  47. }
  48. }
  49. return http.build()
  50. }
  51. ----
  52. .XML
  53. [source,xml,role="secondary"]
  54. ----
  55. <http>
  56. <!-- ... -->
  57. <csrf request-handler-ref="requestHandler"/>
  58. </http>
  59. <b:bean id="requestHandler"
  60. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
  61. p:csrfRequestAttributeName="_csrf"/>
  62. ----
  63. ====
  64. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  65. .Defer Loading `CsrfToken`
  66. ====
  67. .Java
  68. [source,java,role="primary"]
  69. ----
  70. @Bean
  71. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  72. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  73. // set the name of the attribute the CsrfToken will be populated on
  74. requestHandler.setCsrfRequestAttributeName(null);
  75. http
  76. // ...
  77. .csrf((csrf) -> csrf
  78. .csrfTokenRequestHandler(requestHandler)
  79. );
  80. return http.build();
  81. }
  82. ----
  83. .Kotlin
  84. [source,kotlin,role="secondary"]
  85. ----
  86. @Bean
  87. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  88. val requestHandler = CsrfTokenRequestAttributeHandler()
  89. // set the name of the attribute the CsrfToken will be populated on
  90. requestHandler.setCsrfRequestAttributeName(null)
  91. http {
  92. csrf {
  93. csrfTokenRequestHandler = requestHandler
  94. }
  95. }
  96. return http.build()
  97. }
  98. ----
  99. .XML
  100. [source,xml,role="secondary"]
  101. ----
  102. <http>
  103. <!-- ... -->
  104. <csrf request-handler-ref="requestHandler"/>
  105. </http>
  106. <b:bean id="requestHandler"
  107. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
  108. <b:property name="csrfRequestAttributeName">
  109. <b:null/>
  110. </b:property>
  111. </b:bean>
  112. ----
  113. ====
  114. === Explicit Save SecurityContextRepository
  115. In Spring Security 5, the default behavior is for the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to automatically be saved to the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] using the xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`].
  116. Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
  117. Unfortunately, automatic persistence of the `SecurityContext` can surprise users when it is done prior to the request completing (i.e. just prior to committing the `HttpServletResponse`).
  118. It also is complex to keep track of the state to determine if a save is necessary causing unnecessary writes to the `SecurityContextRepository` (i.e. `HttpSession`) at times.
  119. In Spring Security 6, the default behavior is that the xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[`SecurityContextHolderFilter`] will only read the `SecurityContext` from `SecurityContextRepository` and populate it in the `SecurityContextHolder`.
  120. Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
  121. This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
  122. To opt into the new Spring Security 6 default, the following configuration can be used.
  123. include::partial$servlet/architecture/security-context-explicit.adoc[]
  124. [[requestcache-query-optimization]]
  125. === Optimize Querying of `RequestCache`
  126. In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
  127. This means that in a typical setup, that in order to use the xref:servlet/architecture.adoc#requestcache[`RequestCache`] the `HttpSession` is queried on every request.
  128. In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
  129. This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
  130. In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
  131. If you are not overriding the defaults (i.e. using `NullRequestCache`), then the following configuration can be used to explicitly opt into the Spring Security 6 behavior in Spring Security 5.8:
  132. include::partial$servlet/architecture/request-cache-continue.adoc[]
  133. === Use `AuthorizationManager` for Method Security
  134. xref:servlet/authorization/method-security.adoc[Method Security] has been xref:servlet/authorization/method-security.adoc#jc-enable-method-security[simplified] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
  135. Should you run into trouble with making these changes, note that `@EnableGlobalMethodSecurity`, while deprecated, will not be removed in 6.0, allowing you to opt out by sticking with the old annotation.
  136. '''
  137. [[servlet-replace-globalmethodsecurity-with-methodsecurity]]
  138. ==== Replace xref:servlet/authorization/method-security.adoc#jc-enable-global-method-security[global method security] with xref:servlet/authorization/method-security.adoc#jc-enable-method-security[method security]
  139. {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableGlobalMethodSecurity.html[`@EnableGlobalMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] are deprecated in favor of {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[`<method-security>`], respectively.
  140. The new annotation and XML element activate Spring's xref:servlet/authorization/method-security.adoc#jc-enable-method-security[pre-post annotations] by default and use `AuthorizationManager` internally.
  141. This means that the following two listings are functionally equivalent:
  142. ====
  143. .Java
  144. [source,java,role="primary"]
  145. ----
  146. @EnableGlobalMethodSecurity(prePostEnabled = true)
  147. ----
  148. .Kotlin
  149. [source,kotlin,role="secondary"]
  150. ----
  151. @EnableGlobalMethodSecurity(prePostEnabled = true)
  152. ----
  153. .Xml
  154. [source,xml,role="secondary"]
  155. ----
  156. <global-method-security pre-post-enabled="true"/>
  157. ----
  158. ====
  159. and:
  160. ====
  161. .Java
  162. [source,java,role="primary"]
  163. ----
  164. @EnableMethodSecurity
  165. ----
  166. .Kotlin
  167. [source,kotlin,role="secondary"]
  168. ----
  169. @EnableMethodSecurity
  170. ----
  171. .Xml
  172. [source,xml,role="secondary"]
  173. ----
  174. <method-security/>
  175. ----
  176. ====
  177. For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.
  178. For example, a listing like:
  179. ====
  180. .Java
  181. [source,java,role="primary"]
  182. ----
  183. @EnableGlobalMethodSecurity(securedEnabled = true)
  184. ----
  185. .Kotlin
  186. [source,kotlin,role="secondary"]
  187. ----
  188. @EnableGlobalMethodSecurity(securedEnabled = true)
  189. ----
  190. .Xml
  191. [source,xml,role="secondary"]
  192. ----
  193. <global-method-security secured-enabled="true"/>
  194. ----
  195. ====
  196. should change to:
  197. ====
  198. .Java
  199. [source,java,role="primary"]
  200. ----
  201. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  202. ----
  203. .Kotlin
  204. [source,kotlin,role="secondary"]
  205. ----
  206. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  207. ----
  208. .Xml
  209. [source,xml,role="secondary"]
  210. ----
  211. <method-security secured-enabled="true" pre-post-enabled="false"/>
  212. ----
  213. ====
  214. '''
  215. [[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
  216. ==== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
  217. `@EnableMethodSecurity` does not pick up a `PermissionEvaluator`.
  218. This helps keep its API simple.
  219. If you have a custom {security-api-url}org/springframework/security/access/PermissionEvaluator.html[`PermissionEvaluator`] `@Bean`, please change it from:
  220. ====
  221. .Java
  222. [source,java,role="primary"]
  223. ----
  224. @Bean
  225. static PermissionEvaluator permissionEvaluator() {
  226. // ... your evaluator
  227. }
  228. ----
  229. .Kotlin
  230. [source,kotlin,role="secondary"]
  231. ----
  232. companion object {
  233. @Bean
  234. fun permissionEvaluator(): PermissionEvaluator {
  235. // ... your evaluator
  236. }
  237. }
  238. ----
  239. ====
  240. to:
  241. ====
  242. .Java
  243. [source,java,role="primary"]
  244. ----
  245. @Bean
  246. static MethodSecurityExpressionHandler expressionHandler() {
  247. var expressionHandler = new DefaultMethodSecurityExpressionHandler();
  248. expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
  249. return expressionHandler;
  250. }
  251. ----
  252. .Kotlin
  253. [source,kotlin,role="secondary"]
  254. ----
  255. companion object {
  256. @Bean
  257. fun expressionHandler(): MethodSecurityExpressionHandler {
  258. val expressionHandler = DefaultMethodSecurityExpressionHandler
  259. expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
  260. return expressionHandler
  261. }
  262. }
  263. ----
  264. ====
  265. '''
  266. [[servlet-check-for-annotationconfigurationexceptions]]
  267. ==== Check for ``AnnotationConfigurationException``s
  268. `@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  269. If after moving to either you see ``AnnotationConfigurationException``s in your logs, follow the instructions in the exception message to clean up your application's method security annotation usage.
  270. === Use `AuthorizationManager` for Message Security
  271. xref:servlet/integrations/websocket.adoc[Message Security] has been xref:servlet/integrations/websocket.adoc#websocket-configuration[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
  272. Should you run into trouble with making these changes, you can follow the <<servlet-authorizationmanager-messages-opt-out,opt out steps>> at the end of this section.
  273. ==== Ensure all messages have defined authorization rules
  274. The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
  275. xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
  276. To prepare for this, ensure that authorization rules exist are declared for every request.
  277. For example, an application configuration like:
  278. ====
  279. .Java
  280. [source,java,role="primary"]
  281. ----
  282. @Override
  283. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  284. messages
  285. .simpDestMatchers("/user/queue/errors").permitAll()
  286. .simpDestMatchers("/admin/**").hasRole("ADMIN");
  287. }
  288. ----
  289. .Kotlin
  290. [source,kotlin,role="secondary"]
  291. ----
  292. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  293. messages
  294. .simpDestMatchers("/user/queue/errors").permitAll()
  295. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  296. }
  297. ----
  298. .Xml
  299. [source,xml,role="secondary"]
  300. ----
  301. <websocket-message-broker>
  302. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  303. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  304. </websocket-message-broker>
  305. ----
  306. ====
  307. should change to:
  308. ====
  309. .Java
  310. [source,java,role="primary"]
  311. ----
  312. @Override
  313. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  314. messages
  315. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  316. .simpDestMatchers("/user/queue/errors").permitAll()
  317. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  318. .anyMessage().denyAll();
  319. }
  320. ----
  321. .Kotlin
  322. [source,kotlin,role="secondary"]
  323. ----
  324. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  325. messages
  326. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  327. .simpDestMatchers("/user/queue/errors").permitAll()
  328. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  329. .anyMessage().denyAll()
  330. }
  331. ----
  332. .Xml
  333. [source,xml,role="secondary"]
  334. ----
  335. <websocket-message-broker>
  336. <intercept-message type="CONNECT" access="permitAll"/>
  337. <intercept-message type="DISCONNECT" access="permitAll"/>
  338. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  339. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  340. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  341. <intercept-message pattern="/**" access="denyAll"/>
  342. </websocket-message-broker>
  343. ----
  344. ====
  345. ==== Add `@EnableWebSocketSecurity`
  346. [NOTE]
  347. ====
  348. If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
  349. Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
  350. Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
  351. ====
  352. If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
  353. For example, you can add it to your websocket security configuration class, like so:
  354. ====
  355. .Java
  356. [source,java,role="primary"]
  357. ----
  358. @EnableWebSocketSecurity
  359. @Configuration
  360. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  361. // ...
  362. }
  363. ----
  364. .Kotlin
  365. [source,kotlin,role="secondary"]
  366. ----
  367. @EnableWebSocketSecurity
  368. @Configuration
  369. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  370. // ...
  371. }
  372. ----
  373. ====
  374. This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
  375. ==== Use an `AuthorizationManager<Message<?>>` instance
  376. To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
  377. For example, the following application configuration:
  378. ====
  379. .Java
  380. [source,java,role="primary"]
  381. ----
  382. @Override
  383. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  384. messages
  385. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  386. .simpDestMatchers("/user/queue/errors").permitAll()
  387. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  388. .anyMessage().denyAll();
  389. }
  390. ----
  391. .Kotlin
  392. [source,kotlin,role="secondary"]
  393. ----
  394. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  395. messages
  396. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  397. .simpDestMatchers("/user/queue/errors").permitAll()
  398. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  399. .anyMessage().denyAll()
  400. }
  401. ----
  402. .Xml
  403. [source,xml,role="secondary"]
  404. ----
  405. <websocket-message-broker>
  406. <intercept-message type="CONNECT" access="permitAll"/>
  407. <intercept-message type="DISCONNECT" access="permitAll"/>
  408. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  409. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  410. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  411. <intercept-message pattern="/**" access="denyAll"/>
  412. </websocket-message-broker>
  413. ----
  414. ====
  415. changes to:
  416. ====
  417. .Java
  418. [source,java,role="primary"]
  419. ----
  420. @Bean
  421. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  422. messages
  423. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  424. .simpDestMatchers("/user/queue/errors").permitAll()
  425. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  426. .anyMessage().denyAll();
  427. return messages.build();
  428. }
  429. ----
  430. .Kotlin
  431. [source,kotlin,role="secondary"]
  432. ----
  433. @Bean
  434. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  435. messages
  436. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  437. .simpDestMatchers("/user/queue/errors").permitAll()
  438. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  439. .anyMessage().denyAll()
  440. return messages.build()
  441. }
  442. ----
  443. .Xml
  444. [source,xml,role="secondary"]
  445. ----
  446. <websocket-message-broker use-authorization-manager="true">
  447. <intercept-message type="CONNECT" access="permitAll"/>
  448. <intercept-message type="DISCONNECT" access="permitAll"/>
  449. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  450. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  451. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  452. <intercept-message pattern="/**" access="denyAll"/>
  453. </websocket-message-broker>
  454. ----
  455. ====
  456. ==== Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
  457. If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
  458. For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
  459. ====
  460. .Java
  461. [source,java,role="primary"]
  462. ----
  463. @EnableWebSocketSecurity
  464. @Configuration
  465. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  466. // ...
  467. }
  468. ----
  469. .Kotlin
  470. [source,kotlin,role="secondary"]
  471. ----
  472. @EnableWebSocketSecurity
  473. @Configuration
  474. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  475. // ...
  476. }
  477. ----
  478. ====
  479. changes to:
  480. ====
  481. .Java
  482. [source,java,role="primary"]
  483. ----
  484. @EnableWebSocketSecurity
  485. @Configuration
  486. public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
  487. // ...
  488. }
  489. ----
  490. .Kotlin
  491. [source,kotlin,role="secondary"]
  492. ----
  493. @EnableWebSocketSecurity
  494. @Configuration
  495. class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
  496. // ...
  497. }
  498. ----
  499. ====
  500. [[servlet-authorizationmanager-messages-opt-out]]
  501. ==== Opt-out Steps
  502. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  503. ===== I cannot declare an authorization rule for all requests
  504. If you are having trouble setting an `anyRequest` authorization rule of `denyAll`, please use {security-api-url}org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.Builder.Constraint.html#permitAll()[`permitAll`] instead, like so:
  505. ====
  506. .Java
  507. [source,java,role="primary"]
  508. ----
  509. @Bean
  510. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  511. messages
  512. .simpDestMatchers("/user/queue/errors").permitAll()
  513. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  514. // ...
  515. .anyMessage().permitAll();
  516. return messages.build();
  517. }
  518. ----
  519. .Kotlin
  520. [source,kotlin,role="secondary"]
  521. ----
  522. @Bean
  523. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  524. messages
  525. .simpDestMatchers("/user/queue/errors").permitAll()
  526. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  527. // ...
  528. .anyMessage().permitAll();
  529. return messages.build()
  530. }
  531. ----
  532. .Xml
  533. [source,xml,role="secondary"]
  534. ----
  535. <websocket-message-broker use-authorization-manager="true">
  536. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  537. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  538. <!-- ... -->
  539. <intercept-message pattern="/**" access="permitAll"/>
  540. </websocket-message-broker>
  541. ----
  542. ====
  543. ===== I cannot get CSRF working, need some other `AbstractSecurityWebSocketMessageBrokerConfigurer` feature, or am having trouble with `AuthorizationManager`
  544. In the case of Java, you may continue using `AbstractMessageSecurityWebSocketMessageBrokerConfigurer`.
  545. Even though it is deprecated, it will not be removed in 6.0.
  546. In the case of XML, you can opt out of `AuthorizationManager` by setting `use-authorization-manager="false"`:
  547. ====
  548. .Xml
  549. [source,xml,role="secondary"]
  550. ----
  551. <websocket-message-broker>
  552. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  553. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  554. </websocket-message-broker>
  555. ----
  556. ====
  557. to:
  558. ====
  559. .Xml
  560. [source,xml,role="secondary"]
  561. ----
  562. <websocket-message-broker use-authorization-manager="false">
  563. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  564. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  565. </websocket-message-broker>
  566. ----
  567. ====
  568. === Use `AuthorizationManager` for Request Security
  569. xref:servlet/authorization/authorize-requests.adoc[HTTP Request Security] has been xref:servlet/authorization/authorize-http-requests.adoc[simplified] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API].
  570. Should you run into trouble with making these changes, you can follow the <<servlet-authorizationmanager-requests-opt-out,opt out steps>> at the end of this section.
  571. ==== Ensure that all requests have defined authorization rules
  572. In Spring Security 5.8 and earlier, requests with no authorization rule are permitted by default.
  573. It is a stronger security position to deny by default, thus requiring that authorization rules be clearly defined for every endpoint.
  574. As such, in 6.0, Spring Security by default denies any request that is missing an authorization rule.
  575. The simplest way to prepare for this change is to introduce an appropriate {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#anyRequest()[`anyRequest`] rule as the last authorization rule.
  576. The recommendation is {security-api-url}org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.AuthorizedUrl.html#denyAll()[`denyAll`] since that is the implied 6.0 default.
  577. [NOTE]
  578. ====
  579. You may already have an `anyRequest` rule defined that you are happy with in which case this step can be skipped.
  580. ====
  581. Adding `denyAll` to the end looks like changing:
  582. ====
  583. .Java
  584. [source,java,role="primary"]
  585. ----
  586. http
  587. .authorizeRequests((authorize) -> authorize
  588. .filterSecurityInterceptorOncePerRequest(true)
  589. .mvcMatchers("/app/**").hasRole("APP")
  590. // ...
  591. )
  592. // ...
  593. ----
  594. .Kotlin
  595. [source,kotlin,role="secondary"]
  596. ----
  597. http {
  598. authorizeRequests {
  599. filterSecurityInterceptorOncePerRequest = true
  600. authorize("/app/**", hasRole("APP"))
  601. // ...
  602. }
  603. }
  604. ----
  605. .Xml
  606. [source,xml,role="secondary"]
  607. ----
  608. <http once-per-request="true">
  609. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  610. <!-- ... -->
  611. </http>
  612. ----
  613. ====
  614. to:
  615. ====
  616. .Java
  617. [source,java,role="primary"]
  618. ----
  619. http
  620. .authorizeRequests((authorize) -> authorize
  621. .filterSecurityInterceptorOncePerRequest(true)
  622. .mvcMatchers("/app/**").hasRole("APP")
  623. // ...
  624. .anyRequest().denyAll()
  625. )
  626. // ...
  627. ----
  628. .Kotlin
  629. [source,kotlin,role="secondary"]
  630. ----
  631. http {
  632. authorizeRequests {
  633. filterSecurityInterceptorOncePerRequest = true
  634. authorize("/app/**", hasRole("APP"))
  635. // ...
  636. authorize(anyRequest, denyAll)
  637. }
  638. }
  639. ----
  640. .Xml
  641. [source,xml,role="secondary"]
  642. ----
  643. <http once-per-request="true">
  644. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  645. <!-- ... -->
  646. <intercept-url pattern="/**" access="denyAll"/>
  647. </http>
  648. ----
  649. ====
  650. If you have already migrated to `authorizeHttpRequests`, the recommended change is the same.
  651. ==== Switch to `AuthorizationManager`
  652. To opt in to using `AuthorizationManager`, you can use `authorizeHttpRequests` or xref:servlet/appendix/namespace/http.adoc#nsa-http-use-authorization-manager[`use-authorization-manager`] for Java or XML, respectively.
  653. Change:
  654. ====
  655. .Java
  656. [source,java,role="primary"]
  657. ----
  658. http
  659. .authorizeRequests((authorize) -> authorize
  660. .filterSecurityInterceptorOncePerRequest(true)
  661. .mvcMatchers("/app/**").hasRole("APP")
  662. // ...
  663. .anyRequest().denyAll()
  664. )
  665. // ...
  666. ----
  667. .Kotlin
  668. [source,kotlin,role="secondary"]
  669. ----
  670. http {
  671. authorizeRequests {
  672. filterSecurityInterceptorOncePerRequest = true
  673. authorize("/app/**", hasRole("APP"))
  674. // ...
  675. authorize(anyRequest, denyAll)
  676. }
  677. }
  678. ----
  679. .Xml
  680. [source,xml,role="secondary"]
  681. ----
  682. <http once-per-request="true">
  683. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  684. <!-- ... -->
  685. <intercept-url pattern="/**" access="denyAll"/>
  686. </http>
  687. ----
  688. ====
  689. to:
  690. ====
  691. .Java
  692. [source,java,role="primary"]
  693. ----
  694. http
  695. .authorizeHttpRequests((authorize) -> authorize
  696. .shouldFilterAllDispatcherTypes(false)
  697. .mvcMatchers("/app/**").hasRole("APP")
  698. // ...
  699. .anyRequest().denyAll()
  700. )
  701. // ...
  702. ----
  703. .Kotlin
  704. [source,kotlin,role="secondary"]
  705. ----
  706. http {
  707. authorizeHttpRequests {
  708. shouldFilterAllDispatcherTypes = false
  709. authorize("/app/**", hasRole("APP"))
  710. // ...
  711. authorize(anyRequest, denyAll)
  712. }
  713. }
  714. ----
  715. .Xml
  716. [source,xml,role="secondary"]
  717. ----
  718. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  719. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  720. <!-- ... -->
  721. <intercept-url pattern="/**" access="denyAll"/>
  722. </http>
  723. ----
  724. ====
  725. ==== Migrate SpEL expressions to `AuthorizationManager`
  726. For authorization rules, Java tends to be easier to test and maintain than SpEL.
  727. As such, `authorizeHttpRequests` does not have a method for declaring a `String` SpEL.
  728. Instead, you can implement your own `AuthorizationManager` implementation or use `WebExpressionAuthorizationManager`.
  729. For completeness, both options will be demonstrated.
  730. First, if you have the following SpEL:
  731. ====
  732. .Java
  733. [source,java,role="primary"]
  734. ----
  735. http
  736. .authorizeRequests((authorize) -> authorize
  737. .filterSecurityInterceptorOncePerRequest(true)
  738. .mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  739. // ...
  740. .anyRequest().denyAll()
  741. )
  742. // ...
  743. ----
  744. .Kotlin
  745. [source,kotlin,role="secondary"]
  746. ----
  747. http {
  748. authorizeRequests {
  749. filterSecurityInterceptorOncePerRequest = true
  750. authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  751. // ...
  752. authorize(anyRequest, denyAll)
  753. }
  754. }
  755. ----
  756. ====
  757. Then you can compose your own `AuthorizationManager` with Spring Security authorization primitives like so:
  758. ====
  759. .Java
  760. [source,java,role="primary"]
  761. ----
  762. http
  763. .authorizeHttpRequests((authorize) -> authorize
  764. .shouldFilterAllDispatcherTypes(false)
  765. .mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  766. // ...
  767. .anyRequest().denyAll()
  768. )
  769. // ...
  770. ----
  771. .Kotlin
  772. [source,kotlin,role="secondary"]
  773. ----
  774. http {
  775. authorizeHttpRequests {
  776. shouldFilterAllDispatcherTypes = false
  777. authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  778. // ...
  779. authorize(anyRequest, denyAll)
  780. }
  781. }
  782. ----
  783. ====
  784. Or you can use `WebExpressionAuthorizationManager` in the following way:
  785. ====
  786. .Java
  787. [source,java,role="primary"]
  788. ----
  789. http
  790. .authorizeRequests((authorize) -> authorize
  791. .filterSecurityInterceptorOncePerRequest(true)
  792. .mvcMatchers("/complicated/**").access(
  793. new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  794. )
  795. // ...
  796. .anyRequest().denyAll()
  797. )
  798. // ...
  799. ----
  800. .Kotlin
  801. [source,kotlin,role="secondary"]
  802. ----
  803. http {
  804. authorizeRequests {
  805. filterSecurityInterceptorOncePerRequest = true
  806. authorize("/complicated/**", access(
  807. WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  808. )
  809. // ...
  810. authorize(anyRequest, denyAll)
  811. }
  812. }
  813. ----
  814. ====
  815. ==== Switch to filter all dispatcher types
  816. Spring Security 5.8 and earlier only xref:servlet/authorization/architecture.adoc[perform authorization] once per request.
  817. This means that dispatcher types like `FORWARD` and `INCLUDE` that run after `REQUEST` are not secured by default.
  818. It's recommended that Spring Security secure all dispatch types.
  819. As such, in 6.0, Spring Security changes this default.
  820. So, finally, change your authorization rules to filter all dispatcher types.
  821. To do this, change:
  822. ====
  823. .Java
  824. [source,java,role="primary"]
  825. ----
  826. http
  827. .authorizeHttpRequests((authorize) -> authorize
  828. .shouldFilterAllDispatcherTypes(false)
  829. .mvcMatchers("/app/**").hasRole("APP")
  830. // ...
  831. .anyRequest().denyAll()
  832. )
  833. // ...
  834. ----
  835. .Kotlin
  836. [source,kotlin,role="secondary"]
  837. ----
  838. http {
  839. authorizeHttpRequests {
  840. shouldFilterAllDispatcherTypes = false
  841. authorize("/app/**", hasRole("APP"))
  842. // ...
  843. authorize(anyRequest, denyAll)
  844. }
  845. }
  846. ----
  847. .Xml
  848. [source,xml,role="secondary"]
  849. ----
  850. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  851. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  852. <!-- ... -->
  853. <intercept-url pattern="/**" access="denyAll"/>
  854. </http>
  855. ----
  856. ====
  857. to:
  858. ====
  859. .Java
  860. [source,java,role="primary"]
  861. ----
  862. http
  863. .authorizeHttpRequests((authorize) -> authorize
  864. .shouldFilterAllDispatcherTypes(true)
  865. .mvcMatchers("/app/**").hasRole("APP")
  866. // ...
  867. .anyRequest().denyAll()
  868. )
  869. // ...
  870. ----
  871. .Kotlin
  872. [source,kotlin,role="secondary"]
  873. ----
  874. http {
  875. authorizeHttpRequests {
  876. shouldFilterAllDispatcherTypes = true
  877. authorize("/app/**", hasRole("APP"))
  878. // ...
  879. authorize(anyRequest, denyAll)
  880. }
  881. }
  882. ----
  883. .Xml
  884. [source,xml,role="secondary"]
  885. ----
  886. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  887. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  888. <!-- ... -->
  889. <intercept-url pattern="/**" access="denyAll"/>
  890. </http>
  891. ----
  892. ====
  893. [[servlet-authorizationmanager-requests-opt-out]]
  894. ==== Opt-out Steps
  895. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  896. ===== I cannot secure all dispatcher types
  897. If you cannot secure all dispatcher types, first try and declare which dispatcher types should not require authorization like so:
  898. ====
  899. .Java
  900. [source,java,role="primary"]
  901. ----
  902. http
  903. .authorizeHttpRequests((authorize) -> authorize
  904. .shouldFilterAllDispatcherTypes(true)
  905. .dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
  906. .mvcMatchers("/app/**").hasRole("APP")
  907. // ...
  908. .anyRequest().denyAll()
  909. )
  910. // ...
  911. ----
  912. .Kotlin
  913. [source,kotlin,role="secondary"]
  914. ----
  915. http {
  916. authorizeHttpRequests {
  917. shouldFilterAllDispatcherTypes = true
  918. authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
  919. authorize("/app/**", hasRole("APP"))
  920. // ...
  921. authorize(anyRequest, denyAll)
  922. }
  923. }
  924. ----
  925. .Xml
  926. [source,xml,role="secondary"]
  927. ----
  928. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  929. <intercept-url request-matcher-ref="dispatchers"/>
  930. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  931. <!-- ... -->
  932. <intercept-url pattern="/**" access="denyAll"/>
  933. </http>
  934. <bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  935. <constructor-arg>
  936. <util:list value-type="javax.servlet.DispatcherType">
  937. <value>FORWARD</value>
  938. <value>INCLUDE</value>
  939. </util:list>
  940. </constructor-arg>
  941. </bean>
  942. ----
  943. ====
  944. Or, if that doesn't work, then you can explicitly opt out of the behavior by setting `filter-all-dispatcher-types` and `filterAllDispatcherTypes` to `false`:
  945. ====
  946. .Java
  947. [source,java,role="primary"]
  948. ----
  949. http
  950. .authorizeHttpRequests((authorize) -> authorize
  951. .filterAllDispatcherTypes(false)
  952. .mvcMatchers("/app/**").hasRole("APP")
  953. // ...
  954. )
  955. // ...
  956. ----
  957. .Kotlin
  958. [source,kotlin,role="secondary"]
  959. ----
  960. http {
  961. authorizeHttpRequests {
  962. filterAllDispatcherTypes = false
  963. authorize("/messages/**", hasRole("APP"))
  964. // ...
  965. }
  966. }
  967. ----
  968. .Xml
  969. [source,xml,role="secondary"]
  970. ----
  971. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  972. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  973. <!-- ... -->
  974. </http>
  975. ----
  976. ====
  977. or, if you are still using `authorizeRequests` or `use-authorization-manager="false"`, set `oncePerRequest` to `true`:
  978. ====
  979. .Java
  980. [source,java,role="primary"]
  981. ----
  982. http
  983. .authorizeRequests((authorize) -> authorize
  984. .filterSecurityInterceptorOncePerRequest(true)
  985. .mvcMatchers("/app/**").hasRole("APP")
  986. // ...
  987. )
  988. // ...
  989. ----
  990. .Kotlin
  991. [source,kotlin,role="secondary"]
  992. ----
  993. http {
  994. authorizeRequests {
  995. filterSecurityInterceptorOncePerRequest = true
  996. authorize("/messages/**", hasRole("APP"))
  997. // ...
  998. }
  999. }
  1000. ----
  1001. .Xml
  1002. [source,xml,role="secondary"]
  1003. ----
  1004. <http once-per-request="true" use-authorization-manager="false">
  1005. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1006. <!-- ... -->
  1007. </http>
  1008. ----
  1009. ====
  1010. ===== I cannot declare an authorization rule for all requests
  1011. If you are having trouble setting an `anyRequest` authorization rule of `denyAll`, please use {security-api-url}org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.AuthorizedUrl.html#permitAll()[`permitAll`] instead, like so:
  1012. ====
  1013. .Java
  1014. [source,java,role="primary"]
  1015. ----
  1016. http
  1017. .authorizeHttpReqeusts((authorize) -> authorize
  1018. .mvcMatchers("/app/*").hasRole("APP")
  1019. // ...
  1020. .anyRequest().permitAll()
  1021. )
  1022. ----
  1023. .Kotlin
  1024. [source,kotlin,role="secondary"]
  1025. ----
  1026. http {
  1027. authorizeHttpRequests {
  1028. authorize("/app*", hasRole("APP"))
  1029. // ...
  1030. authorize(anyRequest, permitAll)
  1031. }
  1032. }
  1033. ----
  1034. .Xml
  1035. [source,xml,role="secondary"]
  1036. ----
  1037. <http>
  1038. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1039. <!-- ... -->
  1040. <intercept-url pattern="/**" access="permitAll"/>
  1041. </http>
  1042. ----
  1043. ====
  1044. ===== I cannot migrate my SpEL or my `AccessDecisionManager`
  1045. If you are having trouble with SpEL, `AccessDecisionManager`, or there is some other feature that you are needing to keep using in `<http>` or `authorizeRequests`, try the following.
  1046. First, if you still need `authorizeRequests`, you are welcome to keep using it. Even though it is deprecated, it is not removed in 6.0.
  1047. Second, if you still need your custom `access-decision-manager-ref` or have some other reason to opt out of `AuthorizationManager`, do:
  1048. ====
  1049. .Xml
  1050. [source,xml,role="secondary"]
  1051. ----
  1052. <http use-authorization-manager="false">
  1053. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1054. <!-- ... -->
  1055. </http>
  1056. ----
  1057. ====
  1058. == Reactive
  1059. === Use `AuthorizationManager` for Method Security
  1060. 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.
  1061. Should you run into trouble with making these changes, you can follow the
  1062. <<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
  1063. '''
  1064. 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.
  1065. [[reactive-change-to-useauthorizationmanager]]
  1066. ==== Change `useAuthorizationManager` to `true`
  1067. To opt in, change `useAuthorizationManager` to `true` like so:
  1068. ====
  1069. .Java
  1070. [source,java,role="primary"]
  1071. ----
  1072. @EnableReactiveMethodSecurity
  1073. ----
  1074. .Kotlin
  1075. [source,kotlin,role="secondary"]
  1076. ----
  1077. @EnableReactiveMethodSecurity
  1078. ----
  1079. ====
  1080. changes to:
  1081. ====
  1082. .Java
  1083. [source,java,role="primary"]
  1084. ----
  1085. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  1086. ----
  1087. .Kotlin
  1088. [source,kotlin,role="secondary"]
  1089. ----
  1090. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  1091. ----
  1092. ====
  1093. '''
  1094. [[reactive-check-for-annotationconfigurationexceptions]]
  1095. ==== Check for ``AnnotationConfigurationException``s
  1096. `useAuthorizationManager` activates stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  1097. 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.
  1098. [[reactive-authorizationmanager-methods-opt-out]]
  1099. ==== Opt-out Steps
  1100. If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
  1101. ====
  1102. .Java
  1103. [source,java,role="primary"]
  1104. ----
  1105. @EnableReactiveMethodSecurity
  1106. ----
  1107. .Kotlin
  1108. [source,kotlin,role="secondary"]
  1109. ----
  1110. @EnableReactiveMethodSecurity
  1111. ----
  1112. ====
  1113. to:
  1114. ====
  1115. .Java
  1116. [source,java,role="primary"]
  1117. ----
  1118. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  1119. ----
  1120. .Kotlin
  1121. [source,kotlin,role="secondary"]
  1122. ----
  1123. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  1124. ----
  1125. ====