migration.adoc 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049
  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. === Explicit SessionAuthenticationStrategy
  14. In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the `SessionAuthenticationStrategy`.
  15. The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
  16. In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
  17. This means that there is no need to detect when `Authentication` is done and thus the `HttpSession` does not need to be read for every request.
  18. To opt into the new Spring Security 6 default, the following configuration can be used.
  19. .Require Explicit `SessionAuthenticationStrategy` Invocation
  20. ====
  21. .Java
  22. [source,java,role="primary"]
  23. ----
  24. @Bean
  25. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  26. http
  27. // ...
  28. .sessionManagement((sessions) -> sessions
  29. .requireExplicitAuthenticationStrategy(true)
  30. );
  31. return http.build();
  32. }
  33. ----
  34. .Kotlin
  35. [source,kotlin,role="secondary"]
  36. ----
  37. @Bean
  38. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  39. http {
  40. sessionManagement {
  41. requireExplicitAuthenticationStrategy = true
  42. }
  43. }
  44. return http.build()
  45. }
  46. ----
  47. .XML
  48. [source,xml,role="secondary"]
  49. ----
  50. <http>
  51. <!-- ... -->
  52. <session-management authentication-strategy-explicit-invocation="true"/>
  53. </http>
  54. ----
  55. ====
  56. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  57. .Explicit use Spring Security 5.8 defaults for `SessionAuthenticationStrategy`
  58. ====
  59. .Java
  60. [source,java,role="primary"]
  61. ----
  62. @Bean
  63. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  64. http
  65. // ...
  66. .sessionManagement((sessions) -> sessions
  67. .requireExplicitAuthenticationStrategy(false)
  68. );
  69. return http.build();
  70. }
  71. ----
  72. .Kotlin
  73. [source,kotlin,role="secondary"]
  74. ----
  75. @Bean
  76. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  77. http {
  78. sessionManagement {
  79. requireExplicitAuthenticationStrategy = false
  80. }
  81. }
  82. return http.build()
  83. }
  84. ----
  85. .XML
  86. [source,xml,role="secondary"]
  87. ----
  88. <http>
  89. <!-- ... -->
  90. <session-management authentication-strategy-explicit-invocation="false"/>
  91. </http>
  92. ----
  93. ====
  94. === Defer Loading CsrfToken
  95. In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
  96. This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
  97. In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
  98. To opt into the new Spring Security 6 default, the following configuration can be used.
  99. .Defer Loading `CsrfToken`
  100. ====
  101. .Java
  102. [source,java,role="primary"]
  103. ----
  104. @Bean
  105. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  106. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  107. // set the name of the attribute the CsrfToken will be populated on
  108. requestHandler.setCsrfRequestAttributeName("_csrf");
  109. http
  110. // ...
  111. .csrf((csrf) -> csrf
  112. .csrfTokenRequestHandler(requestHandler)
  113. );
  114. return http.build();
  115. }
  116. ----
  117. .Kotlin
  118. [source,kotlin,role="secondary"]
  119. ----
  120. @Bean
  121. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  122. val requestHandler = CsrfTokenRequestAttributeHandler()
  123. // set the name of the attribute the CsrfToken will be populated on
  124. requestHandler.setCsrfRequestAttributeName("_csrf")
  125. http {
  126. csrf {
  127. csrfTokenRequestHandler = requestHandler
  128. }
  129. }
  130. return http.build()
  131. }
  132. ----
  133. .XML
  134. [source,xml,role="secondary"]
  135. ----
  136. <http>
  137. <!-- ... -->
  138. <csrf request-handler-ref="requestHandler"/>
  139. </http>
  140. <b:bean id="requestHandler"
  141. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
  142. p:csrfRequestAttributeName="_csrf"/>
  143. ----
  144. ====
  145. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  146. .Explicit Configure `CsrfToken` with 5.8 Defaults
  147. ====
  148. .Java
  149. [source,java,role="primary"]
  150. ----
  151. @Bean
  152. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  153. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  154. // set the name of the attribute the CsrfToken will be populated on
  155. requestHandler.setCsrfRequestAttributeName(null);
  156. http
  157. // ...
  158. .csrf((csrf) -> csrf
  159. .csrfTokenRequestHandler(requestHandler)
  160. );
  161. return http.build();
  162. }
  163. ----
  164. .Kotlin
  165. [source,kotlin,role="secondary"]
  166. ----
  167. @Bean
  168. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  169. val requestHandler = CsrfTokenRequestAttributeHandler()
  170. // set the name of the attribute the CsrfToken will be populated on
  171. requestHandler.setCsrfRequestAttributeName(null)
  172. http {
  173. csrf {
  174. csrfTokenRequestHandler = requestHandler
  175. }
  176. }
  177. return http.build()
  178. }
  179. ----
  180. .XML
  181. [source,xml,role="secondary"]
  182. ----
  183. <http>
  184. <!-- ... -->
  185. <csrf request-handler-ref="requestHandler"/>
  186. </http>
  187. <b:bean id="requestHandler"
  188. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
  189. <b:property name="csrfRequestAttributeName">
  190. <b:null/>
  191. </b:property>
  192. </b:bean>
  193. ----
  194. ====
  195. === CSRF BREACH Protection
  196. If the steps for <<Defer Loading CsrfToken>> work for you, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
  197. .`CsrfToken` BREACH Protection
  198. ====
  199. .Java
  200. [source,java,role="primary"]
  201. ----
  202. @Bean
  203. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  204. XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
  205. // set the name of the attribute the CsrfToken will be populated on
  206. requestHandler.setCsrfRequestAttributeName("_csrf");
  207. http
  208. // ...
  209. .csrf((csrf) -> csrf
  210. .csrfTokenRequestHandler(requestHandler)
  211. );
  212. return http.build();
  213. }
  214. ----
  215. .Kotlin
  216. [source,kotlin,role="secondary"]
  217. ----
  218. @Bean
  219. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  220. val requestHandler = XorCsrfTokenRequestAttributeHandler()
  221. // set the name of the attribute the CsrfToken will be populated on
  222. requestHandler.setCsrfRequestAttributeName("_csrf")
  223. http {
  224. csrf {
  225. csrfTokenRequestHandler = requestHandler
  226. }
  227. }
  228. return http.build()
  229. }
  230. ----
  231. .XML
  232. [source,xml,role="secondary"]
  233. ----
  234. <http>
  235. <!-- ... -->
  236. <csrf request-handler-ref="requestHandler"/>
  237. </http>
  238. <b:bean id="requestHandler"
  239. class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
  240. p:csrfRequestAttributeName="_csrf"/>
  241. ----
  242. ====
  243. === Explicit Save SecurityContextRepository
  244. 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`].
  245. Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
  246. 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`).
  247. 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.
  248. 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`.
  249. Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
  250. This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
  251. To opt into the new Spring Security 6 default, the following configuration can be used.
  252. include::partial$servlet/architecture/security-context-explicit.adoc[]
  253. === Multiple SecurityContextRepository
  254. In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] is `HttpSessionSecurityContextRepository`.
  255. In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
  256. To opt into the new Spring Security 6 default, the following configuration can be used.
  257. .Configure SecurityContextRepository with 6.0 defaults
  258. ====
  259. .Java
  260. [source,java,role="primary"]
  261. ----
  262. @Bean
  263. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  264. http
  265. // ...
  266. .securityContext((securityContext) -> securityContext
  267. .securityContextRepository(new DelegatingSecurityContextRepository(
  268. new RequestAttributeSecurityContextRepository(),
  269. new HttpSessionSecurityContextRepository()
  270. ))
  271. );
  272. return http.build();
  273. }
  274. ----
  275. .Kotlin
  276. [source,kotlin,role="secondary"]
  277. ----
  278. @Bean
  279. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  280. http {
  281. // ...
  282. securityContext {
  283. securityContextRepository = DelegatingSecurityContextRepository(
  284. RequestAttributeSecurityContextRepository(),
  285. HttpSessionSecurityContextRepository()
  286. )
  287. }
  288. }
  289. return http.build()
  290. }
  291. ----
  292. .XML
  293. [source,xml,role="secondary"]
  294. ----
  295. <http security-context-repository-ref="contextRepository">
  296. <!-- ... -->
  297. </http>
  298. <bean name="contextRepository"
  299. class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
  300. <constructor-arg>
  301. <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  302. </constructor-arg>
  303. <constructor-arg>
  304. <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
  305. </constructor-arg>
  306. </bean>
  307. ----
  308. ====
  309. [IMPORTANT]
  310. ====
  311. If you are already using an implementation other than `HttpSessionSecurityContextRepository`, you should replace it with your chosen implementation in the example above to ensure that it is used along with `RequestAttributeSecurityContextRepository`.
  312. ====
  313. === Deprecation in SecurityContextRepository
  314. In Spring Security 5.7, a new method was added to xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] with the signature:
  315. Supplier<SecurityContext> loadContext(HttpServletRequest request)
  316. With the addition of xref:servlet/authentication/persistence.adoc#delegatingsecuritycontextrepository[`DelegatingSecurityContextRepository`] in Spring Security 5.8, that method was deprecated in favor of a new method with the signature:
  317. DeferredSecurityContext loadDeferredContext(HttpServletRequest request)
  318. In Spring Security 6, the deprecated method was removed.
  319. If you have implemented `SecurityContextRepository` yourself and added an implementation of the `loadContext(request)` method, you can prepare for Spring Security 6 by removing the implementation of that method and implementing the new method instead.
  320. To get started implementing the new method, use the following example that adapts a `Supplier<SecurityContext>` to provide a `DeferredSecurityContext`:
  321. [NOTE]
  322. ====
  323. The adapted `Supplier` should return `null` when no `SecurityContext` is available, which was not the case with the `Supplier` returned from `loadContext(request)`.
  324. ====
  325. .Adapt `Supplier<SecurityContext>` to `DeferredSecurityContext`
  326. ====
  327. .Java
  328. [source,java,role="primary"]
  329. ----
  330. @Override
  331. public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
  332. // Adapt a supplier that returns null when the context is not available
  333. Supplier<SecurityContext> supplier = () -> getContextOrNull(request);
  334. SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
  335. return new DeferredSecurityContext() {
  336. private SecurityContext securityContext;
  337. private boolean isGenerated;
  338. @Override
  339. public SecurityContext get() {
  340. if (this.securityContext == null) {
  341. this.securityContext = supplier.get();
  342. if (this.securityContext == null) {
  343. this.securityContext = strategy.createEmptyContext();
  344. this.isGenerated = true;
  345. }
  346. }
  347. return this.securityContext;
  348. }
  349. @Override
  350. public boolean isGenerated() {
  351. get();
  352. return this.isGenerated;
  353. }
  354. };
  355. }
  356. ----
  357. .Kotlin
  358. [source,kotlin,role="secondary"]
  359. ----
  360. override fun loadDeferredContext(request: HttpServletRequest): DeferredSecurityContext {
  361. // Adapt a supplier that returns null when the context is not available
  362. val supplier: Supplier<SecurityContext?> = SingletonSupplier.of {
  363. getContextOrNull(request)
  364. }
  365. val strategy = SecurityContextHolder.getContextHolderStrategy()
  366. return object : DeferredSecurityContext {
  367. private var securityContext: SecurityContext? = null
  368. private var isGenerated = false
  369. override fun get(): SecurityContext {
  370. if (securityContext == null) {
  371. securityContext = supplier.get()
  372. ?: strategy.createEmptyContext().also { isGenerated = true }
  373. }
  374. return securityContext!!
  375. }
  376. override fun isGenerated(): Boolean {
  377. get()
  378. return isGenerated
  379. }
  380. }
  381. }
  382. ----
  383. ====
  384. [[requestcache-query-optimization]]
  385. === Optimize Querying of `RequestCache`
  386. In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
  387. 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.
  388. In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
  389. This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
  390. In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
  391. 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:
  392. include::partial$servlet/architecture/request-cache-continue.adoc[]
  393. === Use `AuthorizationManager` for Method Security
  394. 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.
  395. 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.
  396. [[servlet-replace-globalmethodsecurity-with-methodsecurity]]
  397. ==== 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]
  398. {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.
  399. 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.
  400. This means that the following two listings are functionally equivalent:
  401. ====
  402. .Java
  403. [source,java,role="primary"]
  404. ----
  405. @EnableGlobalMethodSecurity(prePostEnabled = true)
  406. ----
  407. .Kotlin
  408. [source,kotlin,role="secondary"]
  409. ----
  410. @EnableGlobalMethodSecurity(prePostEnabled = true)
  411. ----
  412. .Xml
  413. [source,xml,role="secondary"]
  414. ----
  415. <global-method-security pre-post-enabled="true"/>
  416. ----
  417. ====
  418. and:
  419. ====
  420. .Java
  421. [source,java,role="primary"]
  422. ----
  423. @EnableMethodSecurity
  424. ----
  425. .Kotlin
  426. [source,kotlin,role="secondary"]
  427. ----
  428. @EnableMethodSecurity
  429. ----
  430. .Xml
  431. [source,xml,role="secondary"]
  432. ----
  433. <method-security/>
  434. ----
  435. ====
  436. For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.
  437. For example, a listing like:
  438. ====
  439. .Java
  440. [source,java,role="primary"]
  441. ----
  442. @EnableGlobalMethodSecurity(securedEnabled = true)
  443. ----
  444. .Kotlin
  445. [source,kotlin,role="secondary"]
  446. ----
  447. @EnableGlobalMethodSecurity(securedEnabled = true)
  448. ----
  449. .Xml
  450. [source,xml,role="secondary"]
  451. ----
  452. <global-method-security secured-enabled="true"/>
  453. ----
  454. ====
  455. should change to:
  456. ====
  457. .Java
  458. [source,java,role="primary"]
  459. ----
  460. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  461. ----
  462. .Kotlin
  463. [source,kotlin,role="secondary"]
  464. ----
  465. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  466. ----
  467. .Xml
  468. [source,xml,role="secondary"]
  469. ----
  470. <method-security secured-enabled="true" pre-post-enabled="false"/>
  471. ----
  472. ====
  473. [[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
  474. ==== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
  475. `@EnableMethodSecurity` does not pick up a `PermissionEvaluator`.
  476. This helps keep its API simple.
  477. If you have a custom {security-api-url}org/springframework/security/access/PermissionEvaluator.html[`PermissionEvaluator`] `@Bean`, please change it from:
  478. ====
  479. .Java
  480. [source,java,role="primary"]
  481. ----
  482. @Bean
  483. static PermissionEvaluator permissionEvaluator() {
  484. // ... your evaluator
  485. }
  486. ----
  487. .Kotlin
  488. [source,kotlin,role="secondary"]
  489. ----
  490. companion object {
  491. @Bean
  492. fun permissionEvaluator(): PermissionEvaluator {
  493. // ... your evaluator
  494. }
  495. }
  496. ----
  497. ====
  498. to:
  499. ====
  500. .Java
  501. [source,java,role="primary"]
  502. ----
  503. @Bean
  504. static MethodSecurityExpressionHandler expressionHandler() {
  505. var expressionHandler = new DefaultMethodSecurityExpressionHandler();
  506. expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
  507. return expressionHandler;
  508. }
  509. ----
  510. .Kotlin
  511. [source,kotlin,role="secondary"]
  512. ----
  513. companion object {
  514. @Bean
  515. fun expressionHandler(): MethodSecurityExpressionHandler {
  516. val expressionHandler = DefaultMethodSecurityExpressionHandler
  517. expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
  518. return expressionHandler
  519. }
  520. }
  521. ----
  522. ====
  523. ==== Replace any custom method-security ``AccessDecisionManager``s
  524. Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement.
  525. The preparation strategy will depend on your reason for each arrangement.
  526. Read on to find the best match for your situation.
  527. ===== I use `UnanimousBased`
  528. If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`] with the default voters, you likely need do nothing since unanimous-based is the default behavior with {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`].
  529. However, if you do discover that you cannot accept the default authorization managers, you can use `AuthorizationManagers.allOf` to compose your own arrangement.
  530. Having done that, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
  531. ===== I use `AffirmativeBased`
  532. If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so:
  533. ====
  534. .Java
  535. [source,java,role="primary"]
  536. ----
  537. AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
  538. // ... your list of authorization managers
  539. )
  540. ----
  541. .Kotlin
  542. [source,kotlin,role="secondary"]
  543. ----
  544. val authorization = AuthorizationManagers.anyOf(
  545. // ... your list of authorization managers
  546. )
  547. ----
  548. ====
  549. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
  550. ===== I use `ConsensusBased`
  551. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  552. In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account.
  553. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
  554. ===== I use a custom `AccessDecisionVoter`
  555. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  556. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  557. By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `@PreAuthorize` would look like:
  558. ====
  559. .Java
  560. [source,java,role="primary"]
  561. ----
  562. public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
  563. private final SecurityMetadataSource metadata;
  564. private final AccessDecisionVoter voter;
  565. public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
  566. ExpressionBasedAnnotationAttributeFactory attributeFactory =
  567. new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
  568. this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
  569. ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
  570. expressionAdvice.setExpressionHandler(expressionHandler);
  571. this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
  572. }
  573. public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
  574. List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
  575. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  576. if (decision == ACCESS_GRANTED) {
  577. return new AuthorizationDecision(true);
  578. }
  579. if (decision == ACCESS_DENIED) {
  580. return new AuthorizationDecision(false);
  581. }
  582. return null; // abstain
  583. }
  584. }
  585. ----
  586. ====
  587. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
  588. ===== I use a custom `AfterInvocationManager`
  589. {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] replaces both {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] and {security-api-url}org/springframework/security/access/intercept/AfterInvocationManager.html[`AfterInvocationManager`].
  590. The difference is that `AuthorizationManager<MethodInvocation>` replaces `AccessDecisionManager` and `AuthorizationManager<MethodInvocationResult>` replaces `AfterInvocationManager`.
  591. Given that, <<_i_use_a_custom_accessdecisionvoter,the same rules apply for adaptation>>, where the goal this time is to implement `AuthorizationManager<MethodInvocationResult>` instead of `AuthorizationManager<MethodInvocation>` and use `AuthorizationManagerAfterMethodInterceptor` instead of `AuthorizationManagerBeforeMethodInterceptor`.
  592. ===== I use `RunAsManager`
  593. There is currently https://github.com/spring-projects/spring-security/issues/11331[no replacement for `RunAsManager`] though one is being considered.
  594. It is quite straightforward to adapt a `RunAsManager`, though, to the `AuthorizationManager` API, if needed.
  595. Here is some pseudocode to get you started:
  596. ====
  597. .Java
  598. [source,java,role="primary"]
  599. ----
  600. public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
  601. private final RunAsManager runAs = new RunAsManagerImpl();
  602. private final SecurityMetadataSource metadata;
  603. private final AuthorizationManager<T> authorization;
  604. // ... constructor
  605. public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
  606. Supplier<Authentication> wrapped = (auth) -> {
  607. List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
  608. return this.runAs.buildRunAs(auth, object, attributes);
  609. };
  610. return this.authorization.check(wrapped, object);
  611. }
  612. }
  613. ----
  614. ====
  615. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
  616. [[servlet-check-for-annotationconfigurationexceptions]]
  617. ==== Check for ``AnnotationConfigurationException``s
  618. `@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  619. 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.
  620. === Use `AuthorizationManager` for Message Security
  621. 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.
  622. 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.
  623. ==== Ensure all messages have defined authorization rules
  624. The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
  625. xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
  626. To prepare for this, ensure that authorization rules exist are declared for every request.
  627. For example, an application configuration like:
  628. ====
  629. .Java
  630. [source,java,role="primary"]
  631. ----
  632. @Override
  633. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  634. messages
  635. .simpDestMatchers("/user/queue/errors").permitAll()
  636. .simpDestMatchers("/admin/**").hasRole("ADMIN");
  637. }
  638. ----
  639. .Kotlin
  640. [source,kotlin,role="secondary"]
  641. ----
  642. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  643. messages
  644. .simpDestMatchers("/user/queue/errors").permitAll()
  645. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  646. }
  647. ----
  648. .Xml
  649. [source,xml,role="secondary"]
  650. ----
  651. <websocket-message-broker>
  652. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  653. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  654. </websocket-message-broker>
  655. ----
  656. ====
  657. should change to:
  658. ====
  659. .Java
  660. [source,java,role="primary"]
  661. ----
  662. @Override
  663. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  664. messages
  665. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  666. .simpDestMatchers("/user/queue/errors").permitAll()
  667. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  668. .anyMessage().denyAll();
  669. }
  670. ----
  671. .Kotlin
  672. [source,kotlin,role="secondary"]
  673. ----
  674. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  675. messages
  676. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  677. .simpDestMatchers("/user/queue/errors").permitAll()
  678. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  679. .anyMessage().denyAll()
  680. }
  681. ----
  682. .Xml
  683. [source,xml,role="secondary"]
  684. ----
  685. <websocket-message-broker>
  686. <intercept-message type="CONNECT" access="permitAll"/>
  687. <intercept-message type="DISCONNECT" access="permitAll"/>
  688. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  689. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  690. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  691. <intercept-message pattern="/**" access="denyAll"/>
  692. </websocket-message-broker>
  693. ----
  694. ====
  695. ==== Add `@EnableWebSocketSecurity`
  696. [NOTE]
  697. ====
  698. If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
  699. Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
  700. Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
  701. ====
  702. If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
  703. For example, you can add it to your websocket security configuration class, like so:
  704. ====
  705. .Java
  706. [source,java,role="primary"]
  707. ----
  708. @EnableWebSocketSecurity
  709. @Configuration
  710. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  711. // ...
  712. }
  713. ----
  714. .Kotlin
  715. [source,kotlin,role="secondary"]
  716. ----
  717. @EnableWebSocketSecurity
  718. @Configuration
  719. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  720. // ...
  721. }
  722. ----
  723. ====
  724. This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
  725. ==== Use an `AuthorizationManager<Message<?>>` instance
  726. To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
  727. For example, the following application configuration:
  728. ====
  729. .Java
  730. [source,java,role="primary"]
  731. ----
  732. @Override
  733. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  734. messages
  735. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  736. .simpDestMatchers("/user/queue/errors").permitAll()
  737. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  738. .anyMessage().denyAll();
  739. }
  740. ----
  741. .Kotlin
  742. [source,kotlin,role="secondary"]
  743. ----
  744. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  745. messages
  746. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  747. .simpDestMatchers("/user/queue/errors").permitAll()
  748. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  749. .anyMessage().denyAll()
  750. }
  751. ----
  752. .Xml
  753. [source,xml,role="secondary"]
  754. ----
  755. <websocket-message-broker>
  756. <intercept-message type="CONNECT" access="permitAll"/>
  757. <intercept-message type="DISCONNECT" access="permitAll"/>
  758. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  759. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  760. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  761. <intercept-message pattern="/**" access="denyAll"/>
  762. </websocket-message-broker>
  763. ----
  764. ====
  765. changes to:
  766. ====
  767. .Java
  768. [source,java,role="primary"]
  769. ----
  770. @Bean
  771. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  772. messages
  773. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  774. .simpDestMatchers("/user/queue/errors").permitAll()
  775. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  776. .anyMessage().denyAll();
  777. return messages.build();
  778. }
  779. ----
  780. .Kotlin
  781. [source,kotlin,role="secondary"]
  782. ----
  783. @Bean
  784. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  785. messages
  786. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  787. .simpDestMatchers("/user/queue/errors").permitAll()
  788. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  789. .anyMessage().denyAll()
  790. return messages.build()
  791. }
  792. ----
  793. .Xml
  794. [source,xml,role="secondary"]
  795. ----
  796. <websocket-message-broker use-authorization-manager="true">
  797. <intercept-message type="CONNECT" access="permitAll"/>
  798. <intercept-message type="DISCONNECT" access="permitAll"/>
  799. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  800. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  801. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  802. <intercept-message pattern="/**" access="denyAll"/>
  803. </websocket-message-broker>
  804. ----
  805. ====
  806. ==== Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
  807. If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
  808. For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
  809. ====
  810. .Java
  811. [source,java,role="primary"]
  812. ----
  813. @EnableWebSocketSecurity
  814. @Configuration
  815. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  816. // ...
  817. }
  818. ----
  819. .Kotlin
  820. [source,kotlin,role="secondary"]
  821. ----
  822. @EnableWebSocketSecurity
  823. @Configuration
  824. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  825. // ...
  826. }
  827. ----
  828. ====
  829. changes to:
  830. ====
  831. .Java
  832. [source,java,role="primary"]
  833. ----
  834. @EnableWebSocketSecurity
  835. @Configuration
  836. public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
  837. // ...
  838. }
  839. ----
  840. .Kotlin
  841. [source,kotlin,role="secondary"]
  842. ----
  843. @EnableWebSocketSecurity
  844. @Configuration
  845. class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
  846. // ...
  847. }
  848. ----
  849. ====
  850. [[servlet-authorizationmanager-messages-opt-out]]
  851. ==== Opt-out Steps
  852. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  853. ===== I cannot declare an authorization rule for all requests
  854. 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:
  855. ====
  856. .Java
  857. [source,java,role="primary"]
  858. ----
  859. @Bean
  860. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  861. messages
  862. .simpDestMatchers("/user/queue/errors").permitAll()
  863. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  864. // ...
  865. .anyMessage().permitAll();
  866. return messages.build();
  867. }
  868. ----
  869. .Kotlin
  870. [source,kotlin,role="secondary"]
  871. ----
  872. @Bean
  873. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  874. messages
  875. .simpDestMatchers("/user/queue/errors").permitAll()
  876. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  877. // ...
  878. .anyMessage().permitAll();
  879. return messages.build()
  880. }
  881. ----
  882. .Xml
  883. [source,xml,role="secondary"]
  884. ----
  885. <websocket-message-broker use-authorization-manager="true">
  886. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  887. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  888. <!-- ... -->
  889. <intercept-message pattern="/**" access="permitAll"/>
  890. </websocket-message-broker>
  891. ----
  892. ====
  893. ===== I cannot get CSRF working, need some other `AbstractSecurityWebSocketMessageBrokerConfigurer` feature, or am having trouble with `AuthorizationManager`
  894. In the case of Java, you may continue using `AbstractMessageSecurityWebSocketMessageBrokerConfigurer`.
  895. Even though it is deprecated, it will not be removed in 6.0.
  896. In the case of XML, you can opt out of `AuthorizationManager` by setting `use-authorization-manager="false"`:
  897. ====
  898. .Xml
  899. [source,xml,role="secondary"]
  900. ----
  901. <websocket-message-broker>
  902. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  903. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  904. </websocket-message-broker>
  905. ----
  906. ====
  907. to:
  908. ====
  909. .Xml
  910. [source,xml,role="secondary"]
  911. ----
  912. <websocket-message-broker use-authorization-manager="false">
  913. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  914. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  915. </websocket-message-broker>
  916. ----
  917. ====
  918. === Use `AuthorizationManager` for Request Security
  919. 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].
  920. 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.
  921. ==== Ensure that all requests have defined authorization rules
  922. In Spring Security 5.8 and earlier, requests with no authorization rule are permitted by default.
  923. It is a stronger security position to deny by default, thus requiring that authorization rules be clearly defined for every endpoint.
  924. As such, in 6.0, Spring Security by default denies any request that is missing an authorization rule.
  925. 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.
  926. 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.
  927. [NOTE]
  928. ====
  929. You may already have an `anyRequest` rule defined that you are happy with in which case this step can be skipped.
  930. ====
  931. Adding `denyAll` to the end looks like changing:
  932. ====
  933. .Java
  934. [source,java,role="primary"]
  935. ----
  936. http
  937. .authorizeRequests((authorize) -> authorize
  938. .filterSecurityInterceptorOncePerRequest(true)
  939. .mvcMatchers("/app/**").hasRole("APP")
  940. // ...
  941. )
  942. // ...
  943. ----
  944. .Kotlin
  945. [source,kotlin,role="secondary"]
  946. ----
  947. http {
  948. authorizeRequests {
  949. filterSecurityInterceptorOncePerRequest = true
  950. authorize("/app/**", hasRole("APP"))
  951. // ...
  952. }
  953. }
  954. ----
  955. .Xml
  956. [source,xml,role="secondary"]
  957. ----
  958. <http once-per-request="true">
  959. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  960. <!-- ... -->
  961. </http>
  962. ----
  963. ====
  964. to:
  965. ====
  966. .Java
  967. [source,java,role="primary"]
  968. ----
  969. http
  970. .authorizeRequests((authorize) -> authorize
  971. .filterSecurityInterceptorOncePerRequest(true)
  972. .mvcMatchers("/app/**").hasRole("APP")
  973. // ...
  974. .anyRequest().denyAll()
  975. )
  976. // ...
  977. ----
  978. .Kotlin
  979. [source,kotlin,role="secondary"]
  980. ----
  981. http {
  982. authorizeRequests {
  983. filterSecurityInterceptorOncePerRequest = true
  984. authorize("/app/**", hasRole("APP"))
  985. // ...
  986. authorize(anyRequest, denyAll)
  987. }
  988. }
  989. ----
  990. .Xml
  991. [source,xml,role="secondary"]
  992. ----
  993. <http once-per-request="true">
  994. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  995. <!-- ... -->
  996. <intercept-url pattern="/**" access="denyAll"/>
  997. </http>
  998. ----
  999. ====
  1000. If you have already migrated to `authorizeHttpRequests`, the recommended change is the same.
  1001. ==== Switch to `AuthorizationManager`
  1002. 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.
  1003. Change:
  1004. ====
  1005. .Java
  1006. [source,java,role="primary"]
  1007. ----
  1008. http
  1009. .authorizeRequests((authorize) -> authorize
  1010. .filterSecurityInterceptorOncePerRequest(true)
  1011. .mvcMatchers("/app/**").hasRole("APP")
  1012. // ...
  1013. .anyRequest().denyAll()
  1014. )
  1015. // ...
  1016. ----
  1017. .Kotlin
  1018. [source,kotlin,role="secondary"]
  1019. ----
  1020. http {
  1021. authorizeRequests {
  1022. filterSecurityInterceptorOncePerRequest = true
  1023. authorize("/app/**", hasRole("APP"))
  1024. // ...
  1025. authorize(anyRequest, denyAll)
  1026. }
  1027. }
  1028. ----
  1029. .Xml
  1030. [source,xml,role="secondary"]
  1031. ----
  1032. <http once-per-request="true">
  1033. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1034. <!-- ... -->
  1035. <intercept-url pattern="/**" access="denyAll"/>
  1036. </http>
  1037. ----
  1038. ====
  1039. to:
  1040. ====
  1041. .Java
  1042. [source,java,role="primary"]
  1043. ----
  1044. http
  1045. .authorizeHttpRequests((authorize) -> authorize
  1046. .shouldFilterAllDispatcherTypes(false)
  1047. .mvcMatchers("/app/**").hasRole("APP")
  1048. // ...
  1049. .anyRequest().denyAll()
  1050. )
  1051. // ...
  1052. ----
  1053. .Kotlin
  1054. [source,kotlin,role="secondary"]
  1055. ----
  1056. http {
  1057. authorizeHttpRequests {
  1058. shouldFilterAllDispatcherTypes = false
  1059. authorize("/app/**", hasRole("APP"))
  1060. // ...
  1061. authorize(anyRequest, denyAll)
  1062. }
  1063. }
  1064. ----
  1065. .Xml
  1066. [source,xml,role="secondary"]
  1067. ----
  1068. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1069. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1070. <!-- ... -->
  1071. <intercept-url pattern="/**" access="denyAll"/>
  1072. </http>
  1073. ----
  1074. ====
  1075. ==== Migrate SpEL expressions to `AuthorizationManager`
  1076. For authorization rules, Java tends to be easier to test and maintain than SpEL.
  1077. As such, `authorizeHttpRequests` does not have a method for declaring a `String` SpEL.
  1078. Instead, you can implement your own `AuthorizationManager` implementation or use `WebExpressionAuthorizationManager`.
  1079. For completeness, both options will be demonstrated.
  1080. First, if you have the following SpEL:
  1081. ====
  1082. .Java
  1083. [source,java,role="primary"]
  1084. ----
  1085. http
  1086. .authorizeRequests((authorize) -> authorize
  1087. .filterSecurityInterceptorOncePerRequest(true)
  1088. .mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  1089. // ...
  1090. .anyRequest().denyAll()
  1091. )
  1092. // ...
  1093. ----
  1094. .Kotlin
  1095. [source,kotlin,role="secondary"]
  1096. ----
  1097. http {
  1098. authorizeRequests {
  1099. filterSecurityInterceptorOncePerRequest = true
  1100. authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  1101. // ...
  1102. authorize(anyRequest, denyAll)
  1103. }
  1104. }
  1105. ----
  1106. ====
  1107. Then you can compose your own `AuthorizationManager` with Spring Security authorization primitives like so:
  1108. ====
  1109. .Java
  1110. [source,java,role="primary"]
  1111. ----
  1112. http
  1113. .authorizeHttpRequests((authorize) -> authorize
  1114. .shouldFilterAllDispatcherTypes(false)
  1115. .mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  1116. // ...
  1117. .anyRequest().denyAll()
  1118. )
  1119. // ...
  1120. ----
  1121. .Kotlin
  1122. [source,kotlin,role="secondary"]
  1123. ----
  1124. http {
  1125. authorizeHttpRequests {
  1126. shouldFilterAllDispatcherTypes = false
  1127. authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  1128. // ...
  1129. authorize(anyRequest, denyAll)
  1130. }
  1131. }
  1132. ----
  1133. ====
  1134. Or you can use `WebExpressionAuthorizationManager` in the following way:
  1135. ====
  1136. .Java
  1137. [source,java,role="primary"]
  1138. ----
  1139. http
  1140. .authorizeRequests((authorize) -> authorize
  1141. .filterSecurityInterceptorOncePerRequest(true)
  1142. .mvcMatchers("/complicated/**").access(
  1143. new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  1144. )
  1145. // ...
  1146. .anyRequest().denyAll()
  1147. )
  1148. // ...
  1149. ----
  1150. .Kotlin
  1151. [source,kotlin,role="secondary"]
  1152. ----
  1153. http {
  1154. authorizeRequests {
  1155. filterSecurityInterceptorOncePerRequest = true
  1156. authorize("/complicated/**", access(
  1157. WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  1158. )
  1159. // ...
  1160. authorize(anyRequest, denyAll)
  1161. }
  1162. }
  1163. ----
  1164. ====
  1165. ==== Switch to filter all dispatcher types
  1166. Spring Security 5.8 and earlier only xref:servlet/authorization/architecture.adoc[perform authorization] once per request.
  1167. This means that dispatcher types like `FORWARD` and `INCLUDE` that run after `REQUEST` are not secured by default.
  1168. It's recommended that Spring Security secure all dispatch types.
  1169. As such, in 6.0, Spring Security changes this default.
  1170. So, finally, change your authorization rules to filter all dispatcher types.
  1171. To do this, change:
  1172. ====
  1173. .Java
  1174. [source,java,role="primary"]
  1175. ----
  1176. http
  1177. .authorizeHttpRequests((authorize) -> authorize
  1178. .shouldFilterAllDispatcherTypes(false)
  1179. .mvcMatchers("/app/**").hasRole("APP")
  1180. // ...
  1181. .anyRequest().denyAll()
  1182. )
  1183. // ...
  1184. ----
  1185. .Kotlin
  1186. [source,kotlin,role="secondary"]
  1187. ----
  1188. http {
  1189. authorizeHttpRequests {
  1190. shouldFilterAllDispatcherTypes = false
  1191. authorize("/app/**", hasRole("APP"))
  1192. // ...
  1193. authorize(anyRequest, denyAll)
  1194. }
  1195. }
  1196. ----
  1197. .Xml
  1198. [source,xml,role="secondary"]
  1199. ----
  1200. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1201. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1202. <!-- ... -->
  1203. <intercept-url pattern="/**" access="denyAll"/>
  1204. </http>
  1205. ----
  1206. ====
  1207. to:
  1208. ====
  1209. .Java
  1210. [source,java,role="primary"]
  1211. ----
  1212. http
  1213. .authorizeHttpRequests((authorize) -> authorize
  1214. .shouldFilterAllDispatcherTypes(true)
  1215. .mvcMatchers("/app/**").hasRole("APP")
  1216. // ...
  1217. .anyRequest().denyAll()
  1218. )
  1219. // ...
  1220. ----
  1221. .Kotlin
  1222. [source,kotlin,role="secondary"]
  1223. ----
  1224. http {
  1225. authorizeHttpRequests {
  1226. shouldFilterAllDispatcherTypes = true
  1227. authorize("/app/**", hasRole("APP"))
  1228. // ...
  1229. authorize(anyRequest, denyAll)
  1230. }
  1231. }
  1232. ----
  1233. .Xml
  1234. [source,xml,role="secondary"]
  1235. ----
  1236. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1237. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1238. <!-- ... -->
  1239. <intercept-url pattern="/**" access="denyAll"/>
  1240. </http>
  1241. ----
  1242. ====
  1243. ==== Replace any custom filter-security ``AccessDecisionManager``s
  1244. Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement.
  1245. The preparation strategy will depend on your reason for each arrangement.
  1246. Read on to find the best match for your situation.
  1247. ===== I use `UnanimousBased`
  1248. If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`], you should first adapt or replace any ``AccessDecisionVoter``s and then you can construct an `AuthorizationManager` like so:
  1249. ====
  1250. .Java
  1251. [source,java,role="primary"]
  1252. ----
  1253. @Bean
  1254. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  1255. PolicyAuthorizationManager policy = ...;
  1256. LocalAuthorizationManager local = ...;
  1257. return AuthorizationMangers.allOf(policy, local);
  1258. }
  1259. ----
  1260. .Kotlin
  1261. [source,kotlin,role="secondary"]
  1262. ----
  1263. @Bean
  1264. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  1265. val policy: PolicyAuthorizationManager = ...
  1266. val local: LocalAuthorizationManager = ...
  1267. return AuthorizationMangers.allOf(policy, local)
  1268. }
  1269. ----
  1270. .Xml
  1271. [source,xml,role="secondary"]
  1272. ----
  1273. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1274. factory-method="allOf">
  1275. <constructor-arg>
  1276. <util:list>
  1277. <bean class="my.PolicyAuthorizationManager"/>
  1278. <bean class="my.LocalAuthorizationManager"/>
  1279. </util:list>
  1280. </constructor-arg>
  1281. </bean>
  1282. ----
  1283. ====
  1284. then, wire it into the DSL like so:
  1285. ====
  1286. .Java
  1287. [source,java,role="primary"]
  1288. ----
  1289. http
  1290. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1291. // ...
  1292. ----
  1293. .Kotlin
  1294. [source,kotlin,role="secondary"]
  1295. ----
  1296. http {
  1297. authorizeHttpRequests {
  1298. authorize(anyRequest, requestAuthorization)
  1299. }
  1300. // ...
  1301. }
  1302. ----
  1303. .Xml
  1304. [source,xml,role="secondary"]
  1305. ----
  1306. <http authorization-manager-ref="requestAuthorization"/>
  1307. ----
  1308. ====
  1309. [NOTE]
  1310. ====
  1311. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1312. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1313. ====
  1314. ===== I use `AffirmativeBased`
  1315. If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so:
  1316. ====
  1317. .Java
  1318. [source,java,role="primary"]
  1319. ----
  1320. @Bean
  1321. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  1322. PolicyAuthorizationManager policy = ...;
  1323. LocalAuthorizationManager local = ...;
  1324. return AuthorizationMangers.anyOf(policy, local);
  1325. }
  1326. ----
  1327. .Kotlin
  1328. [source,kotlin,role="secondary"]
  1329. ----
  1330. @Bean
  1331. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  1332. val policy: PolicyAuthorizationManager = ...
  1333. val local: LocalAuthorizationManager = ...
  1334. return AuthorizationMangers.anyOf(policy, local)
  1335. }
  1336. ----
  1337. .Xml
  1338. [source,xml,role="secondary"]
  1339. ----
  1340. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1341. factory-method="anyOf">
  1342. <constructor-arg>
  1343. <util:list>
  1344. <bean class="my.PolicyAuthorizationManager"/>
  1345. <bean class="my.LocalAuthorizationManager"/>
  1346. </util:list>
  1347. </constructor-arg>
  1348. </bean>
  1349. ----
  1350. ====
  1351. then, wire it into the DSL like so:
  1352. ====
  1353. .Java
  1354. [source,java,role="primary"]
  1355. ----
  1356. http
  1357. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1358. // ...
  1359. ----
  1360. .Kotlin
  1361. [source,kotlin,role="secondary"]
  1362. ----
  1363. http {
  1364. authorizeHttpRequests {
  1365. authorize(anyRequest, requestAuthorization)
  1366. }
  1367. // ...
  1368. }
  1369. ----
  1370. .Xml
  1371. [source,xml,role="secondary"]
  1372. ----
  1373. <http authorization-manager-ref="requestAuthorization"/>
  1374. ----
  1375. ====
  1376. [NOTE]
  1377. ====
  1378. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1379. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1380. ====
  1381. ===== I use `ConsensusBased`
  1382. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  1383. In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account.
  1384. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`].
  1385. ===== I use a custom `AccessDecisionVoter`
  1386. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  1387. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  1388. By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `anyRequest().authenticated()` would look like:
  1389. ====
  1390. .Java
  1391. [source,java,role="primary"]
  1392. ----
  1393. public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
  1394. private final SecurityMetadataSource metadata;
  1395. private final AccessDecisionVoter voter;
  1396. public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
  1397. Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
  1398. AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
  1399. this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
  1400. WebExpressionVoter voter = new WebExpressionVoter();
  1401. voter.setExpressionHandler(expressionHandler);
  1402. this.voter = voter;
  1403. }
  1404. public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
  1405. List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
  1406. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  1407. if (decision == ACCESS_GRANTED) {
  1408. return new AuthorizationDecision(true);
  1409. }
  1410. if (decision == ACCESS_DENIED) {
  1411. return new AuthorizationDecision(false);
  1412. }
  1413. return null; // abstain
  1414. }
  1415. }
  1416. ----
  1417. ====
  1418. Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`].
  1419. [[servlet-authorizationmanager-requests-opt-out]]
  1420. ==== Opt-out Steps
  1421. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  1422. ===== I cannot secure all dispatcher types
  1423. If you cannot secure all dispatcher types, first try and declare which dispatcher types should not require authorization like so:
  1424. ====
  1425. .Java
  1426. [source,java,role="primary"]
  1427. ----
  1428. http
  1429. .authorizeHttpRequests((authorize) -> authorize
  1430. .shouldFilterAllDispatcherTypes(true)
  1431. .dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
  1432. .mvcMatchers("/app/**").hasRole("APP")
  1433. // ...
  1434. .anyRequest().denyAll()
  1435. )
  1436. // ...
  1437. ----
  1438. .Kotlin
  1439. [source,kotlin,role="secondary"]
  1440. ----
  1441. http {
  1442. authorizeHttpRequests {
  1443. shouldFilterAllDispatcherTypes = true
  1444. authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
  1445. authorize("/app/**", hasRole("APP"))
  1446. // ...
  1447. authorize(anyRequest, denyAll)
  1448. }
  1449. }
  1450. ----
  1451. .Xml
  1452. [source,xml,role="secondary"]
  1453. ----
  1454. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1455. <intercept-url request-matcher-ref="dispatchers"/>
  1456. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1457. <!-- ... -->
  1458. <intercept-url pattern="/**" access="denyAll"/>
  1459. </http>
  1460. <bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  1461. <constructor-arg>
  1462. <util:list value-type="javax.servlet.DispatcherType">
  1463. <value>FORWARD</value>
  1464. <value>INCLUDE</value>
  1465. </util:list>
  1466. </constructor-arg>
  1467. </bean>
  1468. ----
  1469. ====
  1470. 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`:
  1471. ====
  1472. .Java
  1473. [source,java,role="primary"]
  1474. ----
  1475. http
  1476. .authorizeHttpRequests((authorize) -> authorize
  1477. .filterAllDispatcherTypes(false)
  1478. .mvcMatchers("/app/**").hasRole("APP")
  1479. // ...
  1480. )
  1481. // ...
  1482. ----
  1483. .Kotlin
  1484. [source,kotlin,role="secondary"]
  1485. ----
  1486. http {
  1487. authorizeHttpRequests {
  1488. filterAllDispatcherTypes = false
  1489. authorize("/messages/**", hasRole("APP"))
  1490. // ...
  1491. }
  1492. }
  1493. ----
  1494. .Xml
  1495. [source,xml,role="secondary"]
  1496. ----
  1497. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1498. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1499. <!-- ... -->
  1500. </http>
  1501. ----
  1502. ====
  1503. or, if you are still using `authorizeRequests` or `use-authorization-manager="false"`, set `oncePerRequest` to `true`:
  1504. ====
  1505. .Java
  1506. [source,java,role="primary"]
  1507. ----
  1508. http
  1509. .authorizeRequests((authorize) -> authorize
  1510. .filterSecurityInterceptorOncePerRequest(true)
  1511. .mvcMatchers("/app/**").hasRole("APP")
  1512. // ...
  1513. )
  1514. // ...
  1515. ----
  1516. .Kotlin
  1517. [source,kotlin,role="secondary"]
  1518. ----
  1519. http {
  1520. authorizeRequests {
  1521. filterSecurityInterceptorOncePerRequest = true
  1522. authorize("/messages/**", hasRole("APP"))
  1523. // ...
  1524. }
  1525. }
  1526. ----
  1527. .Xml
  1528. [source,xml,role="secondary"]
  1529. ----
  1530. <http once-per-request="true" use-authorization-manager="false">
  1531. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1532. <!-- ... -->
  1533. </http>
  1534. ----
  1535. ====
  1536. ===== I cannot declare an authorization rule for all requests
  1537. 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:
  1538. ====
  1539. .Java
  1540. [source,java,role="primary"]
  1541. ----
  1542. http
  1543. .authorizeHttpReqeusts((authorize) -> authorize
  1544. .mvcMatchers("/app/*").hasRole("APP")
  1545. // ...
  1546. .anyRequest().permitAll()
  1547. )
  1548. ----
  1549. .Kotlin
  1550. [source,kotlin,role="secondary"]
  1551. ----
  1552. http {
  1553. authorizeHttpRequests {
  1554. authorize("/app*", hasRole("APP"))
  1555. // ...
  1556. authorize(anyRequest, permitAll)
  1557. }
  1558. }
  1559. ----
  1560. .Xml
  1561. [source,xml,role="secondary"]
  1562. ----
  1563. <http>
  1564. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1565. <!-- ... -->
  1566. <intercept-url pattern="/**" access="permitAll"/>
  1567. </http>
  1568. ----
  1569. ====
  1570. ===== I cannot migrate my SpEL or my `AccessDecisionManager`
  1571. 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.
  1572. 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.
  1573. Second, if you still need your custom `access-decision-manager-ref` or have some other reason to opt out of `AuthorizationManager`, do:
  1574. ====
  1575. .Xml
  1576. [source,xml,role="secondary"]
  1577. ----
  1578. <http use-authorization-manager="false">
  1579. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1580. <!-- ... -->
  1581. </http>
  1582. ----
  1583. ====
  1584. === Propagate ``AuthenticationServiceException``s
  1585. {security-api-url}org/springframework/security/web/authentication/AuthenticationFilter.html[`AuthenticationFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/authentication/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`].
  1586. 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.
  1587. ==== Configure `AuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  1588. To prepare for the 6.0 default, wire `AuthenticationFilter` instances with a `AuthenticationFailureHandler` that rethrows ``AuthenticationServiceException``s, like so:
  1589. ====
  1590. .Java
  1591. [source,java,role="primary"]
  1592. ----
  1593. AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
  1594. AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
  1595. handler.setRethrowAuthenticationServiceException(true);
  1596. authenticationFilter.setAuthenticationFailureHandler(handler);
  1597. ----
  1598. .Kotlin
  1599. [source,kotlin,role="secondary"]
  1600. ----
  1601. val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
  1602. val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
  1603. handler.setRethrowAuthenticationServiceException(true)
  1604. authenticationFilter.setAuthenticationFailureHandler(handler)
  1605. ----
  1606. .Xml
  1607. [source,xml,role="secondary"]
  1608. ----
  1609. <bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
  1610. <!-- ... -->
  1611. <property ref="authenticationFailureHandler"/>
  1612. </bean>
  1613. <bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
  1614. <property name="rethrowAuthenticationServiceException" value="true"/>
  1615. </bean>
  1616. ----
  1617. ====
  1618. [[servlet-authenticationfailurehandler-opt-out]]
  1619. ==== Opt-out Steps
  1620. If rethrowing ``AuthenticationServiceException``s gives you trouble, you can set the value to false instead of taking the 6.0 default, like so:
  1621. ====
  1622. .Java
  1623. [source,java,role="primary"]
  1624. ----
  1625. AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
  1626. AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
  1627. handler.setRethrowAuthenticationServiceException(false);
  1628. authenticationFilter.setAuthenticationFailureHandler(handler);
  1629. ----
  1630. .Kotlin
  1631. [source,kotlin,role="secondary"]
  1632. ----
  1633. val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
  1634. val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
  1635. handler.setRethrowAuthenticationServiceException(false)
  1636. authenticationFilter.setAuthenticationFailureHandler(handler)
  1637. ----
  1638. .Xml
  1639. [source,xml,role="secondary"]
  1640. ----
  1641. <bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
  1642. <!-- ... -->
  1643. <property ref="authenticationFailureHandler"/>
  1644. </bean>
  1645. <bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
  1646. <property name="rethrowAuthenticationServiceException" value="false"/>
  1647. </bean>
  1648. ----
  1649. ====
  1650. [[servlet-opt-in-sha256-rememberme]]
  1651. === Use SHA-256 in Remember Me
  1652. The `TokenBasedRememberMeServices` implementation now supports SHA-256 for the Remember Me token and this is the default in Spring Security 6.
  1653. This change makes the implementation more secure by default since MD5 is already proven to be a weak hashing algorithm and vulnerable against collision attacks and modular differential attacks.
  1654. The new generated tokens now have the information of which algorithm was used to generate the token and that information is used in order to match it.
  1655. If the algorithm name is not present, then the `matchingAlgorithm` property is used to check the token.
  1656. This allows for a smooth transition from MD5 to SHA-256.
  1657. To opt into the new Spring Security 6 default to encode the tokens while still being able to decode tokens encoded with MD5, you can set the `encodingAlgorithm` property to SHA-256 and the `matchingAlgorithm` property to MD5.
  1658. See the xref:servlet/authentication/rememberme.adoc#_tokenbasedremembermeservices[reference documentation] and the {security-api-url}org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.html[API docs] for more information.
  1659. [[servlet-opt-in-sha256-sha256-encoding]]
  1660. .Use Spring Security 6 defaults for encoding, SHA-256 for encoding and MD5 for matching
  1661. ====
  1662. .Java
  1663. [source,java,role="primary"]
  1664. ----
  1665. @Configuration
  1666. @EnableWebSecurity
  1667. public class SecurityConfig {
  1668. @Bean
  1669. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1670. http
  1671. // ...
  1672. .rememberMe((remember) -> remember
  1673. .rememberMeServices(rememberMeServices)
  1674. );
  1675. return http.build();
  1676. }
  1677. @Bean
  1678. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1679. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
  1680. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1681. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
  1682. return rememberMe;
  1683. }
  1684. }
  1685. ----
  1686. .XML
  1687. [source,xml,role="secondary"]
  1688. ----
  1689. <http>
  1690. <remember-me services-ref="rememberMeServices"/>
  1691. </http>
  1692. <bean id="rememberMeServices" class=
  1693. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1694. <property name="userDetailsService" ref="myUserDetailsService"/>
  1695. <property name="key" value="springRocks"/>
  1696. <property name="matchingAlgorithm" value="MD5"/>
  1697. <property name="encodingAlgorithm" value="SHA256"/>
  1698. </bean>
  1699. ----
  1700. ====
  1701. At some point, you will want to fully migrate to Spring Security 6 defaults. But how do you know when it is safe to do so?
  1702. Let's suppose that you deployed your application using SHA-256 as the encoding algorithm (as you have done <<servlet-opt-in-sha256-sha256-encoding,here>>) on November 1st, if you have the value for the `tokenValiditySeconds` property set to N days (14 is the default), you can migrate to SHA-256 N days after November 1st (which is November 15th in this example).
  1703. By that time, all the tokens generated with MD5 will have expired.
  1704. .Use Spring Security 6 defaults, SHA-256 for both encoding and matching
  1705. ====
  1706. .Java
  1707. [source,java,role="primary"]
  1708. ----
  1709. @Configuration
  1710. @EnableWebSecurity
  1711. public class SecurityConfig {
  1712. @Bean
  1713. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1714. http
  1715. // ...
  1716. .rememberMe((remember) -> remember
  1717. .rememberMeServices(rememberMeServices)
  1718. );
  1719. return http.build();
  1720. }
  1721. @Bean
  1722. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1723. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
  1724. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1725. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
  1726. return rememberMe;
  1727. }
  1728. }
  1729. ----
  1730. .XML
  1731. [source,xml,role="secondary"]
  1732. ----
  1733. <http>
  1734. <remember-me services-ref="rememberMeServices"/>
  1735. </http>
  1736. <bean id="rememberMeServices" class=
  1737. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1738. <property name="userDetailsService" ref="myUserDetailsService"/>
  1739. <property name="key" value="springRocks"/>
  1740. <property name="matchingAlgorithm" value="SHA256"/>
  1741. <property name="encodingAlgorithm" value="SHA256"/>
  1742. </bean>
  1743. ----
  1744. ====
  1745. If you are having problems with the Spring Security 6 defaults, you can explicitly opt into 5.8 defaults using the following configuration:
  1746. .Use MD5 for both encoding and matching algorithms
  1747. ====
  1748. .Java
  1749. [source,java,role="primary"]
  1750. ----
  1751. @Configuration
  1752. @EnableWebSecurity
  1753. public class SecurityConfig {
  1754. @Bean
  1755. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1756. http
  1757. // ...
  1758. .rememberMe((remember) -> remember
  1759. .rememberMeServices(rememberMeServices)
  1760. );
  1761. return http.build();
  1762. }
  1763. @Bean
  1764. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1765. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.MD5;
  1766. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1767. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
  1768. return rememberMe;
  1769. }
  1770. }
  1771. ----
  1772. .XML
  1773. [source,xml,role="secondary"]
  1774. ----
  1775. <http>
  1776. <remember-me services-ref="rememberMeServices"/>
  1777. </http>
  1778. <bean id="rememberMeServices" class=
  1779. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1780. <property name="userDetailsService" ref="myUserDetailsService"/>
  1781. <property name="key" value="springRocks"/>
  1782. <property name="matchingAlgorithm" value="MD5"/>
  1783. <property name="encodingAlgorithm" value="MD5"/>
  1784. </bean>
  1785. ----
  1786. ====
  1787. === Stop Using SAML 2.0 `Converter` constructors
  1788. In an early release of Spring Security's SAML 2.0 support, `Saml2MetadataFilter` and `Saml2AuthenticationTokenConverter` shipped with constructors of type `Converter`.
  1789. This level of abstraction made it tricky to evolve the class and so a dedicated interface `RelyingPartyRegistrationResolver` was introduced in a later release.
  1790. In 6.0, the `Converter` constructors are removed.
  1791. To prepare for this in 5.8, change classes that implement `Converter<HttpServletRequest, RelyingPartyRegistration>` to instead implement `RelyingPartyRegistrationResolver`.
  1792. === Change to Using `Saml2AuthenticationRequestResolver`
  1793. `Saml2AuthenticationContextResolver` and `Saml2AuthenticationRequestFactory` are removed in 6.0 as is the `Saml2WebSsoAuthenticationRequestFilter` that requires them.
  1794. They are replaced by `Saml2AuthenticationRequestResolver` and a new constructor in `Saml2WebSsoAuthenticationRequestFilter`.
  1795. The new interface removes an unnecessary transport object between the two classes.
  1796. Most applications need do nothing; however, if you use or configure `Saml2AuthenticationRequestContextResolver` or `Saml2AuthenticationRequestFactory`, try the following steps to convert instead use `Saml2AuthenticationRequestResolver`.
  1797. ==== Use `setAuthnRequestCustomizer` instead of `setAuthenticationRequestContextConverter`
  1798. If you are calling `OpenSaml4AuthenticationReqeustFactory#setAuthenticationRequestContextConverter`, for example, like so:
  1799. ====
  1800. .Java
  1801. [source,java,role="primary"]
  1802. ----
  1803. @Bean
  1804. Saml2AuthenticationRequestFactory authenticationRequestFactory() {
  1805. OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
  1806. factory.setAuthenticationRequestContextConverter((context) -> {
  1807. AuthnRequestBuilder authnRequestBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
  1808. .getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
  1809. IssuerBuilder issuerBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
  1810. .getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
  1811. tring issuer = context.getIssuer();
  1812. String destination = context.getDestination();
  1813. String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl();
  1814. String protocolBinding = context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
  1815. AuthnRequest auth = authnRequestBuilder.buildObject();
  1816. auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
  1817. auth.setIssueInstant(Instant.now());
  1818. auth.setForceAuthn(Boolean.TRUE);
  1819. auth.setIsPassive(Boolean.FALSE);
  1820. auth.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
  1821. Issuer iss = issuerBuilder.buildObject();
  1822. iss.setValue(issuer);
  1823. auth.setIssuer(iss);
  1824. auth.setDestination(destination);
  1825. auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl);
  1826. });
  1827. return factory;
  1828. }
  1829. ----
  1830. ====
  1831. to ensure that ForceAuthn is set to `true`, you can instead do:
  1832. ====
  1833. .Java
  1834. [source,java,role="primary"]
  1835. ----
  1836. @Bean
  1837. Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationResolver registrations) {
  1838. OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
  1839. resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest().setForceAuthn(Boolean.TRUE));
  1840. return resolver;
  1841. }
  1842. ----
  1843. ====
  1844. Also, since `setAuthnRequestCustomizer` has direct access to the `HttpServletRequest`, there is no need for a `Saml2AuthenticationRequestContextResolver`.
  1845. Simply use `setAuthnRequestCustomizer` to read directly from `HttpServletRequest` this information you need.
  1846. ==== Use `setAuthnRequestCustomizer` instead of `setProtocolBinding`
  1847. Instead of doing:
  1848. ====
  1849. .Java
  1850. [source,java,role="primary"]
  1851. ----
  1852. @Bean
  1853. Saml2AuthenticationRequestFactory authenticationRequestFactory() {
  1854. OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
  1855. factory.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
  1856. return factory;
  1857. }
  1858. ----
  1859. ====
  1860. you can do:
  1861. ====
  1862. .Java
  1863. [source,java,role="primary"]
  1864. ----
  1865. @Bean
  1866. Saml2AuthenticationRequestResolver authenticationRequestResolver() {
  1867. OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
  1868. resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest()
  1869. .setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"));
  1870. return resolver;
  1871. }
  1872. ----
  1873. ====
  1874. [NOTE]
  1875. ====
  1876. Since Spring Security only supports the `POST` binding for authentication, there is not very much value in overriding the protocol binding at this point in time.
  1877. ====
  1878. === Use the latest `Saml2AuthenticationToken` constructor
  1879. In an early release, `Saml2AuthenticationToken` took several individual settings as constructor parameters.
  1880. This created a challenge each time a new parameter needed to be added.
  1881. Since most of these settings were part of `RelyingPartyRegistration`, a new constructor was added where a `RelyingPartyRegistration` could be provided, making the constructor more stable.
  1882. It also is valuable in that it more closely aligns with the design of `OAuth2LoginAuthenticationToken`.
  1883. Most applications do not construct this class directly since `Saml2WebSsoAuthenticationFilter` does.
  1884. However, in the event that your application constructs one, please change from:
  1885. ====
  1886. .Java
  1887. [source,java,role="primary"]
  1888. ----
  1889. new Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
  1890. registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
  1891. ----
  1892. .Kotlin
  1893. [source,kotlin,role="secondary"]
  1894. ----
  1895. Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
  1896. registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
  1897. ----
  1898. ====
  1899. to:
  1900. ====
  1901. .Java
  1902. [source,java,role="primary"]
  1903. ----
  1904. new Saml2AuthenticationToken(saml2Response, registration)
  1905. ----
  1906. .Kotlin
  1907. [source,kotlin,role="secondary"]
  1908. ----
  1909. Saml2AuthenticationToken(saml2Response, registration)
  1910. ----
  1911. ====
  1912. === Use `RelyingPartyRegistration` updated methods
  1913. In an early release of Spring Security's SAML support, there was some ambiguity on the meaning of certain `RelyingPartyRegistration` methods and their function.
  1914. As more capabilities were added to `RelyingPartyRegistration`, it became necessary to clarify this ambiguity by changing method names to ones that aligned with spec language.
  1915. The deprecated methods in `RelyingPartyRegstration` are removed.
  1916. To prepare for that, consider the following representative usage of `RelyingPartyRegistration`:
  1917. ====
  1918. .Java
  1919. [source,java,role="primary"]
  1920. ----
  1921. String idpEntityId = registration.getRemoteIdpEntityId();
  1922. String assertionConsumerServiceUrl = registration.getAssertionConsumerServiceUrlTemplate();
  1923. String idpWebSsoUrl = registration.getIdpWebSsoUrl();
  1924. String localEntityId = registration.getLocalEntityIdTemplate();
  1925. List<Saml2X509Credential> verifying = registration.getCredentials().stream()
  1926. .filter(Saml2X509Credential::isSignatureVerficationCredential)
  1927. .collect(Collectors.toList());
  1928. ----
  1929. .Kotlin
  1930. [source,kotlin,role="secondary"]
  1931. ----
  1932. val idpEntityId: String = registration.getRemoteIdpEntityId()
  1933. val assertionConsumerServiceUrl: String = registration.getAssertionConsumerServiceUrlTemplate()
  1934. val idpWebSsoUrl: String = registration.getIdpWebSsoUrl()
  1935. val localEntityId: String = registration.getLocalEntityIdTemplate()
  1936. val verifying: List<Saml2X509Credential> = registration.getCredentials()
  1937. .filter(Saml2X509Credential::isSignatureVerficationCredential)
  1938. ----
  1939. ====
  1940. This should change to:
  1941. ====
  1942. .Java
  1943. [source,java,role="primary"]
  1944. ----
  1945. String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId();
  1946. String assertionConsumerServiceLocation = registration.getAssertionConsumerServiceLocation();
  1947. String singleSignOnServiceLocation = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation();
  1948. String entityId = registration.getEntityId();
  1949. List<Saml2X509Credential> verifying = registration.getAssertingPartyDetails().getVerificationX509Credentials();
  1950. ----
  1951. .Kotlin
  1952. [source,kotlin,role="secondary"]
  1953. ----
  1954. val assertingPartyEntityId: String = registration.getAssertingPartyDetails().getEntityId()
  1955. val assertionConsumerServiceLocation: String = registration.getAssertionConsumerServiceLocation()
  1956. val singleSignOnServiceLocation: String = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation()
  1957. val entityId: String = registration.getEntityId()
  1958. val verifying: List<Saml2X509Credential> = registration.getAssertingPartyDetails().getVerificationX509Credentials()
  1959. ----
  1960. ====
  1961. For a complete listing of all changed methods, please see {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[``RelyingPartyRegistration``'s JavaDoc].
  1962. === Use OpenSAML 4
  1963. OpenSAML 3 has reached its end-of-life.
  1964. As such, Spring Security 6 drops support for it, bumping up its OpenSAML baseline to 4.
  1965. To prepare for the upgrade, update your pom to depend on OpenSAML 4 instead of 3:
  1966. ====
  1967. .Maven
  1968. [source,maven,role="primary"]
  1969. ----
  1970. <dependencyManagement>
  1971. <dependency>
  1972. <groupId>org.opensaml</groupId>
  1973. <artifactId>opensaml-core</artifactId>
  1974. <version>4.2.1</version>
  1975. </dependency>
  1976. <dependency>
  1977. <groupId>org.opensaml</groupId>
  1978. <artifactId>opensaml-saml-api</artifactId>
  1979. <version>4.2.1</version>
  1980. </dependency>
  1981. <dependency>
  1982. <groupId>org.opensaml</groupId>
  1983. <artifactId>opensaml-saml-impl</artifactId>
  1984. <version>4.2.1</version>
  1985. </dependency>
  1986. </dependencyManagement>
  1987. ----
  1988. .Gradle
  1989. [source,gradle,role="secondary"]
  1990. ----
  1991. dependencies {
  1992. constraints {
  1993. api "org.opensaml:opensaml-core:4.2.1"
  1994. api "org.opensaml:opensaml-saml-api:4.2.1"
  1995. api "org.opensaml:opensaml-saml-impl:4.2.1"
  1996. }
  1997. }
  1998. ----
  1999. ====
  2000. You must use at least OpenSAML 4.1.1 to update to Spring Security 6's SAML support.
  2001. === Use `OpenSaml4AuthenticationProvider`
  2002. In order to support both OpenSAML 3 and 4 at the same time, Spring Security released `OpenSamlAuthenticationProvider` and `OpenSaml4AuthenticationProvider`.
  2003. In 6.0, because OpenSAML3 support is removed, `OpenSamlAuthenticationProvider` is removed as well.
  2004. Not all methods in `OpenSamlAuthenticationProvider` were ported 1-to-1 to `OpenSaml4AuthenticationProvider`.
  2005. As such, some adjustment will be required to make the challenge.
  2006. Consider the following representative usage of `OpenSamlAuthenticationProvider`:
  2007. ====
  2008. .Java
  2009. [source,java,role="primary"]
  2010. ----
  2011. OpenSamlAuthenticationProvider versionThree = new OpenSamlAuthenticationProvider();
  2012. versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor);
  2013. versionThree.setResponseTimeValidationSkew(myDuration);
  2014. ----
  2015. .Kotlin
  2016. [source,kotlin,role="secondary"]
  2017. ----
  2018. val versionThree: OpenSamlAuthenticationProvider = OpenSamlAuthenticationProvider()
  2019. versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor)
  2020. versionThree.setResponseTimeValidationSkew(myDuration)
  2021. ----
  2022. ====
  2023. This should change to:
  2024. ====
  2025. .Java
  2026. [source,java,role="primary"]
  2027. ----
  2028. Converter<ResponseToken, Saml2Authentication> delegate = OpenSaml4AuthenticationProvider
  2029. .createDefaultResponseAuthenticationConverter();
  2030. OpenSaml4AuthenticationProvider versionFour = new OpenSaml4AuthenticationProvider();
  2031. versionFour.setResponseAuthenticationConverter((responseToken) -> {
  2032. Saml2Authentication authentication = delegate.convert(responseToken);
  2033. Assertion assertion = responseToken.getResponse().getAssertions().get(0);
  2034. AuthenticatedPrincipal principal = (AuthenticatedPrincipal) authentication.getPrincipal();
  2035. Collection<GrantedAuthority> authorities = myAuthoritiesExtractor.convert(assertion);
  2036. return new Saml2Authentication(principal, authentication.getSaml2Response(), authorities);
  2037. });
  2038. Converter<AssertionToken, Saml2ResponseValidationResult> validator = OpenSaml4AuthenticationProvider
  2039. .createDefaultAssertionValidatorWithParameters((p) -> p.put(CLOCK_SKEW, myDuration));
  2040. versionFour.setAssertionValidator(validator);
  2041. ----
  2042. .Kotlin
  2043. [source,kotlin,role="secondary"]
  2044. ----
  2045. val delegate = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
  2046. val versionFour = OpenSaml4AuthenticationProvider()
  2047. versionFour.setResponseAuthenticationConverter({
  2048. responseToken -> {
  2049. val authentication = delegate.convert(responseToken)
  2050. val assertion = responseToken.getResponse().getAssertions().get(0)
  2051. val principal = (AuthenticatedPrincipal) authentication.getPrincipal()
  2052. val authorities = myAuthoritiesExtractor.convert(assertion)
  2053. return Saml2Authentication(principal, authentication.getSaml2Response(), authorities)
  2054. }
  2055. })
  2056. val validator = OpenSaml4AuthenticationProvider
  2057. .createDefaultAssertionValidatorWithParameters({ p -> p.put(CLOCK_SKEW, myDuration) })
  2058. versionFour.setAssertionValidator(validator)
  2059. ----
  2060. ====
  2061. [[use-new-requestmatchers]]
  2062. === Use the new `requestMatchers` methods
  2063. In Spring Security 5.8, the {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#antMatchers(java.lang.String...)[`antMatchers`], {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#mvcMatchers(java.lang.String...)[`mvcMatchers`], and {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#regexMatchers(java.lang.String...)[`regexMatchers`] methods were deprecated in favor of new xref::servlet/authorization/authorize-http-requests.adoc#_request_matchers[`requestMatchers` methods].
  2064. The new `requestMatchers` methods were added xref::servlet/authorization/authorize-http-requests.adoc[to `authorizeHttpRequests`], `authorizeRequests`, CSRF configuration, `WebSecurityCustomizer` and any other places that had the specialized `RequestMatcher` methods.
  2065. The deprecated methods are removed in Spring Security 6.
  2066. These new methods have more secure defaults since they choose the most appropriate `RequestMatcher` implementation for your application.
  2067. In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods).
  2068. To start using the new methods, you can replace the deprecated methods with the new ones. For example, the following application configuration:
  2069. ====
  2070. .Java
  2071. [source,java,role="primary"]
  2072. ----
  2073. @Configuration
  2074. @EnableWebSecurity
  2075. public class SecurityConfig {
  2076. @Bean
  2077. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2078. http
  2079. .authorizeHttpRequests((authz) -> authz
  2080. .antMatchers("/api/admin/**").hasRole("ADMIN")
  2081. .antMatchers("/api/user/**").hasRole("USER")
  2082. .anyRequest().authenticated()
  2083. );
  2084. return http.build();
  2085. }
  2086. }
  2087. ----
  2088. ====
  2089. can be changed to:
  2090. ====
  2091. .Java
  2092. [source,java,role="primary"]
  2093. ----
  2094. @Configuration
  2095. @EnableWebSecurity
  2096. public class SecurityConfig {
  2097. @Bean
  2098. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2099. http
  2100. .authorizeHttpRequests((authz) -> authz
  2101. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2102. .requestMatchers("/api/user/**").hasRole("USER")
  2103. .anyRequest().authenticated()
  2104. );
  2105. return http.build();
  2106. }
  2107. }
  2108. ----
  2109. ====
  2110. If you have Spring MVC in the classpath and are using the `mvcMatchers` methods, you can replace it with the new methods and Spring Security will choose the `MvcRequestMatcher` implementation for you.
  2111. The following configuration:
  2112. ====
  2113. .Java
  2114. [source,java,role="primary"]
  2115. ----
  2116. @Configuration
  2117. @EnableWebSecurity
  2118. @EnableWebMvc
  2119. public class SecurityConfig {
  2120. @Bean
  2121. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2122. http
  2123. .authorizeHttpRequests((authz) -> authz
  2124. .mvcMatchers("/admin/**").hasRole("ADMIN")
  2125. .anyRequest().authenticated()
  2126. );
  2127. return http.build();
  2128. }
  2129. }
  2130. ----
  2131. ====
  2132. is equivalent to:
  2133. ====
  2134. .Java
  2135. [source,java,role="primary"]
  2136. ----
  2137. @Configuration
  2138. @EnableWebSecurity
  2139. @EnableWebMvc
  2140. public class SecurityConfig {
  2141. @Bean
  2142. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2143. http
  2144. .authorizeHttpRequests((authz) -> authz
  2145. .requestMatchers("/admin/**").hasRole("ADMIN")
  2146. .anyRequest().authenticated()
  2147. );
  2148. return http.build();
  2149. }
  2150. }
  2151. ----
  2152. ====
  2153. If you are customizing the `servletPath` property of the `MvcRequestMatcher`, you can now use the `MvcRequestMatcher.Builder` to create `MvcRequestMatcher` instances that share the same servlet path:
  2154. ====
  2155. .Java
  2156. [source,java,role="primary"]
  2157. ----
  2158. @Configuration
  2159. @EnableWebSecurity
  2160. @EnableWebMvc
  2161. public class SecurityConfig {
  2162. @Bean
  2163. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2164. http
  2165. .authorizeHttpRequests((authz) -> authz
  2166. .mvcMatchers("/admin").servletPath("/path").hasRole("ADMIN")
  2167. .mvcMatchers("/user").servletPath("/path").hasRole("USER")
  2168. .anyRequest().authenticated()
  2169. );
  2170. return http.build();
  2171. }
  2172. }
  2173. ----
  2174. ====
  2175. The code above can be rewritten using the `MvcRequestMatcher.Builder` and the `requestMatchers` method:
  2176. ====
  2177. .Java
  2178. [source,java,role="primary"]
  2179. ----
  2180. @Configuration
  2181. @EnableWebSecurity
  2182. @EnableWebMvc
  2183. public class SecurityConfig {
  2184. @Bean
  2185. SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
  2186. MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
  2187. http
  2188. .authorizeHttpRequests((authz) -> authz
  2189. .requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
  2190. .requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
  2191. .anyRequest().authenticated()
  2192. );
  2193. return http.build();
  2194. }
  2195. }
  2196. ----
  2197. ====
  2198. If you are having problem with the new `requestMatchers` methods, you can always switch back to the `RequestMatcher` implementation that you were using.
  2199. For example, if you still want to use `AntPathRequestMatcher` and `RegexRequestMatcher` implementations, you can use the `requestMatchers` method that accepts a `RequestMatcher` instance:
  2200. ====
  2201. .Java
  2202. [source,java,role="primary"]
  2203. ----
  2204. import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
  2205. import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;
  2206. @Configuration
  2207. @EnableWebSecurity
  2208. public class SecurityConfig {
  2209. @Bean
  2210. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2211. http
  2212. .authorizeHttpRequests((authz) -> authz
  2213. .requestMatchers(antMatcher("/user/**")).hasRole("USER")
  2214. .requestMatchers(antMatcher(HttpMethod.POST, "/user/**")).hasRole("ADMIN")
  2215. .requestMatchers(regexMatcher(".*\\?x=y")).hasRole("SPECIAL") // matches /any/path?x=y
  2216. .anyRequest().authenticated()
  2217. );
  2218. return http.build();
  2219. }
  2220. }
  2221. ----
  2222. ====
  2223. Note that the above sample uses static factory methods from {security-api-url}org/springframework/security/web/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] and {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to improve readability.
  2224. If you are using the `WebSecurityCustomizer` interface, you can replace the deprecated `antMatchers` methods:
  2225. ====
  2226. .Java
  2227. [source,java,role="primary"]
  2228. ----
  2229. @Bean
  2230. public WebSecurityCustomizer webSecurityCustomizer() {
  2231. return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
  2232. }
  2233. ----
  2234. ====
  2235. with their `requestMatchers` counterparts:
  2236. ====
  2237. .Java
  2238. [source,java,role="primary"]
  2239. ----
  2240. @Bean
  2241. public WebSecurityCustomizer webSecurityCustomizer() {
  2242. return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2");
  2243. }
  2244. ----
  2245. ====
  2246. The same way, if you are customizing the CSRF configuration to ignore some paths, you can replace the deprecated methods with the `requestMatchers` methods:
  2247. ====
  2248. .Java
  2249. [source,java,role="primary"]
  2250. ----
  2251. @Bean
  2252. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2253. http
  2254. .csrf((csrf) -> csrf
  2255. .ignoringAntMatchers("/no-csrf")
  2256. );
  2257. return http.build();
  2258. }
  2259. ----
  2260. ====
  2261. can be changed to:
  2262. ====
  2263. .Java
  2264. [source,java,role="primary"]
  2265. ----
  2266. @Bean
  2267. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2268. http
  2269. .csrf((csrf) -> csrf
  2270. .ignoringRequestMatchers("/no-csrf")
  2271. );
  2272. return http.build();
  2273. }
  2274. ----
  2275. ====
  2276. [[use-new-security-matchers]]
  2277. === Use the new `securityMatchers` methods
  2278. In Spring Security 5.8, the `antMatchers`, `mvcMatchers` and `requestMatchers` methods from `HttpSecurity` were deprecated in favor of new `securityMatchers` methods.
  2279. Note that these methods are not the same from `authorizeHttpRequests` methods <<use-new-requestmatchers,which were deprecated>> in favor of the `requestMatchers` methods.
  2280. However, the `securityMatchers` methods are similar to the `requestMatchers` methods in the sense that they will choose the most appropriate `RequestMatcher` implementation for your application.
  2281. In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods).
  2282. Another reason for adding the `securityMatchers` methods is to avoid confusion with the `requestMatchers` methods from `authorizeHttpRequests`.
  2283. The following configuration:
  2284. ====
  2285. .Java
  2286. [source,java,role="primary"]
  2287. ----
  2288. @Bean
  2289. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2290. http
  2291. .antMatcher("/api/**", "/app/**")
  2292. .authorizeHttpRequests((authz) -> authz
  2293. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2294. .anyRequest().authenticated()
  2295. );
  2296. return http.build();
  2297. }
  2298. ----
  2299. ====
  2300. can be rewritten using the `securityMatchers` methods:
  2301. ====
  2302. .Java
  2303. [source,java,role="primary"]
  2304. ----
  2305. @Bean
  2306. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2307. http
  2308. .securityMatcher("/api/**", "/app/**")
  2309. .authorizeHttpRequests((authz) -> authz
  2310. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2311. .anyRequest().authenticated()
  2312. );
  2313. return http.build();
  2314. }
  2315. ----
  2316. ====
  2317. If you are using a custom `RequestMatcher` in your `HttpSecurity` configuration:
  2318. ====
  2319. .Java
  2320. [source,java,role="primary"]
  2321. ----
  2322. @Bean
  2323. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2324. http
  2325. .requestMatcher(new MyCustomRequestMatcher())
  2326. .authorizeHttpRequests((authz) -> authz
  2327. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2328. .anyRequest().authenticated()
  2329. );
  2330. return http.build();
  2331. }
  2332. public class MyCustomRequestMatcher implements RequestMatcher {
  2333. // ...
  2334. }
  2335. ----
  2336. ====
  2337. you can do the same using `securityMatcher`:
  2338. ====
  2339. .Java
  2340. [source,java,role="primary"]
  2341. ----
  2342. @Bean
  2343. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2344. http
  2345. .securityMatcher(new MyCustomRequestMatcher())
  2346. .authorizeHttpRequests((authz) -> authz
  2347. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2348. .anyRequest().authenticated()
  2349. );
  2350. return http.build();
  2351. }
  2352. public class MyCustomRequestMatcher implements RequestMatcher {
  2353. // ...
  2354. }
  2355. ----
  2356. ====
  2357. If you are combining multiple `RequestMatcher` implementations in your `HttpSecurity` configuration:
  2358. ====
  2359. .Java
  2360. [source,java,role="primary"]
  2361. ----
  2362. @Bean
  2363. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2364. http
  2365. .requestMatchers((matchers) -> matchers
  2366. .antMatchers("/api/**", "/app/**")
  2367. .mvcMatchers("/admin/**")
  2368. .requestMatchers(new MyCustomRequestMatcher())
  2369. )
  2370. .authorizeHttpRequests((authz) -> authz
  2371. .requestMatchers("/admin/**").hasRole("ADMIN")
  2372. .anyRequest().authenticated()
  2373. );
  2374. return http.build();
  2375. }
  2376. ----
  2377. ====
  2378. you can change it by using `securityMatchers`:
  2379. ====
  2380. .Java
  2381. [source,java,role="primary"]
  2382. ----
  2383. @Bean
  2384. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2385. http
  2386. .securityMatchers((matchers) -> matchers
  2387. .requestMatchers("/api/**", "/app/**", "/admin/**")
  2388. .requestMatchers(new MyCustomRequestMatcher())
  2389. )
  2390. .authorizeHttpRequests((authz) -> authz
  2391. .requestMatchers("/admin/**").hasRole("ADMIN")
  2392. .anyRequest().authenticated()
  2393. );
  2394. return http.build();
  2395. }
  2396. ----
  2397. ====
  2398. If you are having problems with the `securityMatchers` methods choosing the `RequestMatcher` implementation for you, you can always choose the `RequestMatcher` implementation yourself:
  2399. ====
  2400. .Java
  2401. [source,java,role="primary"]
  2402. ----
  2403. import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
  2404. @Bean
  2405. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2406. http
  2407. .securityMatcher(antMatcher("/api/**"), antMatcher("/app/**"))
  2408. .authorizeHttpRequests((authz) -> authz
  2409. .requestMatchers(antMatcher("/api/admin/**")).hasRole("ADMIN")
  2410. .anyRequest().authenticated()
  2411. );
  2412. return http.build();
  2413. }
  2414. ----
  2415. ====
  2416. === Stop using `Encryptors.queryableText`
  2417. `Encryptors.queryableText(CharSequence,CharSequence)` is unsafe since https://tanzu.vmware.com/security/cve-2020-5408[the same input data will produce the same output].
  2418. It was deprecated and will be removed in 6.0; Spring Security no longer supports encrypting data in this way.
  2419. To upgrade, you will either need to re-encrypt with a supported mechanism or store it decrypted.
  2420. Consider the following pseudocode for reading each encrypted entry from a table, decrypting it, and then re-encrypting it using a supported mechanism:
  2421. ====
  2422. .Java
  2423. [source,java,role="primary"]
  2424. ----
  2425. TextEncryptor deprecated = Encryptors.queryableText(password, salt);
  2426. BytesEncryptor aes = new AesBytesEncryptor(password, salt, KeyGenerators.secureRandom(12), CipherAlgorithm.GCM);
  2427. TextEncryptor supported = new HexEncodingTextEncryptor(aes);
  2428. for (MyEntry entry : entries) {
  2429. String value = deprecated.decrypt(entry.getEncryptedValue()); <1>
  2430. entry.setEncryptedValue(supported.encrypt(value)); <2>
  2431. entryService.save(entry)
  2432. }
  2433. ----
  2434. ====
  2435. <1> - The above uses the deprecated `queryableText` to convert the value to plaintext.
  2436. <2> - Then, the value is re-encrypted with a supported Spring Security mechanism.
  2437. Please see the reference manual for more information on what xref:features/integrations/cryptography.adoc[encryption mechanisms Spring Security supports].
  2438. == Reactive
  2439. === Use `AuthorizationManager` for Method Security
  2440. 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.
  2441. Should you run into trouble with making these changes, you can follow the
  2442. <<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
  2443. 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.
  2444. [[reactive-change-to-useauthorizationmanager]]
  2445. ==== Change `useAuthorizationManager` to `true`
  2446. To opt in, change `useAuthorizationManager` to `true` like so:
  2447. ====
  2448. .Java
  2449. [source,java,role="primary"]
  2450. ----
  2451. @EnableReactiveMethodSecurity
  2452. ----
  2453. .Kotlin
  2454. [source,kotlin,role="secondary"]
  2455. ----
  2456. @EnableReactiveMethodSecurity
  2457. ----
  2458. ====
  2459. changes to:
  2460. ====
  2461. .Java
  2462. [source,java,role="primary"]
  2463. ----
  2464. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  2465. ----
  2466. .Kotlin
  2467. [source,kotlin,role="secondary"]
  2468. ----
  2469. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  2470. ----
  2471. ====
  2472. [[reactive-check-for-annotationconfigurationexceptions]]
  2473. ==== Check for ``AnnotationConfigurationException``s
  2474. `useAuthorizationManager` activates stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  2475. 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.
  2476. [[reactive-authorizationmanager-methods-opt-out]]
  2477. ==== Opt-out Steps
  2478. If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
  2479. ====
  2480. .Java
  2481. [source,java,role="primary"]
  2482. ----
  2483. @EnableReactiveMethodSecurity
  2484. ----
  2485. .Kotlin
  2486. [source,kotlin,role="secondary"]
  2487. ----
  2488. @EnableReactiveMethodSecurity
  2489. ----
  2490. ====
  2491. to:
  2492. ====
  2493. .Java
  2494. [source,java,role="primary"]
  2495. ----
  2496. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  2497. ----
  2498. .Kotlin
  2499. [source,kotlin,role="secondary"]
  2500. ----
  2501. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  2502. ----
  2503. ====
  2504. === Propagate ``AuthenticationServiceException``s
  2505. {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`].
  2506. 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.
  2507. ==== Configure `ServerAuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  2508. To prepare for the 6.0 default, `httpBasic` and `oauth2ResourceServer` should be configured to rethrow ``AuthenticationServiceException``s.
  2509. For each, construct the appropriate authentication entry point for `httpBasic` and for `oauth2ResourceServer`:
  2510. ====
  2511. .Java
  2512. [source,java,role="primary"]
  2513. ----
  2514. ServerAuthenticationEntryPoint bearerEntryPoint = new BearerTokenServerAuthenticationEntryPoint();
  2515. ServerAuthenticationEntryPoint basicEntryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);
  2516. ----
  2517. .Kotlin
  2518. [source,kotlin,role="secondary"]
  2519. ----
  2520. val bearerEntryPoint: ServerAuthenticationEntryPoint = BearerTokenServerAuthenticationEntryPoint()
  2521. val basicEntryPoint: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
  2522. ----
  2523. ====
  2524. [NOTE]
  2525. ====
  2526. If you use a custom `AuthenticationEntryPoint` for either or both mechanisms, use that one instead for the remaining steps.
  2527. ====
  2528. Then, construct and configure a `ServerAuthenticationEntryPointFailureHandler` for each one:
  2529. ====
  2530. .Java
  2531. [source,java,role="primary"]
  2532. ----
  2533. AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
  2534. bearerFailureHandler.setRethrowAuthenticationServiceException(true);
  2535. AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
  2536. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  2537. ----
  2538. .Kotlin
  2539. [source,kotlin,role="secondary"]
  2540. ----
  2541. val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
  2542. bearerFailureHandler.setRethrowAuthenticationServiceException(true)
  2543. val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
  2544. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  2545. ----
  2546. ====
  2547. Finally, wire each authentication failure handler into the DSL, like so:
  2548. ====
  2549. .Java
  2550. [source,java,role="primary"]
  2551. ----
  2552. http
  2553. .httpBasic((basic) -> basic.authenticationFailureHandler(basicFailureHandler))
  2554. .oauth2ResourceServer((oauth2) -> oauth2.authenticationFailureHandler(bearerFailureHandler))
  2555. ----
  2556. .Kotlin
  2557. [source,kotlin,role="secondary"]
  2558. ----
  2559. http {
  2560. httpBasic {
  2561. authenticationFailureHandler = basicFailureHandler
  2562. }
  2563. oauth2ResourceServer {
  2564. authenticationFailureHandler = bearerFailureHandler
  2565. }
  2566. }
  2567. ----
  2568. ====
  2569. [[reactive-authenticationfailurehandler-opt-out]]
  2570. ==== Opt-out Steps
  2571. 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.