migration.adoc 134 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299
  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. == Update to Spring Security 5.8
  13. The first step is to ensure you are the latest patch release of Spring Boot 2.7.
  14. Next, you should ensure you are on the latest patch release of Spring Security 5.8.
  15. If you are using Spring Boot, you will need to override the Spring Boot version from Spring Security 5.7 to 5.8.
  16. Spring Security 5.8 is fully compatible with Spring Security 5.7 and thus Spring Boot 2.7.
  17. For directions, on how to update to Spring Security 5.8 visit the xref::getting-spring-security.adoc[] section of the reference guide.
  18. == Servlet
  19. === Explicit SessionAuthenticationStrategy
  20. In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the `SessionAuthenticationStrategy`.
  21. The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
  22. In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
  23. 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.
  24. To opt into the new Spring Security 6 default, the following configuration can be used.
  25. .Require Explicit `SessionAuthenticationStrategy` Invocation
  26. ====
  27. .Java
  28. [source,java,role="primary"]
  29. ----
  30. @Bean
  31. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  32. http
  33. // ...
  34. .sessionManagement((sessions) -> sessions
  35. .requireExplicitAuthenticationStrategy(true)
  36. );
  37. return http.build();
  38. }
  39. ----
  40. .Kotlin
  41. [source,kotlin,role="secondary"]
  42. ----
  43. @Bean
  44. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  45. http {
  46. sessionManagement {
  47. requireExplicitAuthenticationStrategy = true
  48. }
  49. }
  50. return http.build()
  51. }
  52. ----
  53. .XML
  54. [source,xml,role="secondary"]
  55. ----
  56. <http>
  57. <!-- ... -->
  58. <session-management authentication-strategy-explicit-invocation="true"/>
  59. </http>
  60. ----
  61. ====
  62. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  63. .Explicit use Spring Security 5.8 defaults for `SessionAuthenticationStrategy`
  64. ====
  65. .Java
  66. [source,java,role="primary"]
  67. ----
  68. @Bean
  69. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  70. http
  71. // ...
  72. .sessionManagement((sessions) -> sessions
  73. .requireExplicitAuthenticationStrategy(false)
  74. );
  75. return http.build();
  76. }
  77. ----
  78. .Kotlin
  79. [source,kotlin,role="secondary"]
  80. ----
  81. @Bean
  82. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  83. http {
  84. sessionManagement {
  85. requireExplicitAuthenticationStrategy = false
  86. }
  87. }
  88. return http.build()
  89. }
  90. ----
  91. .XML
  92. [source,xml,role="secondary"]
  93. ----
  94. <http>
  95. <!-- ... -->
  96. <session-management authentication-strategy-explicit-invocation="false"/>
  97. </http>
  98. ----
  99. ====
  100. === Defer Loading CsrfToken
  101. In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
  102. This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
  103. In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
  104. To opt into the new Spring Security 6 default, the following configuration can be used.
  105. .Defer Loading `CsrfToken`
  106. ====
  107. .Java
  108. [source,java,role="primary"]
  109. ----
  110. @Bean
  111. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  112. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  113. // set the name of the attribute the CsrfToken will be populated on
  114. requestHandler.setCsrfRequestAttributeName("_csrf");
  115. http
  116. // ...
  117. .csrf((csrf) -> csrf
  118. .csrfTokenRequestHandler(requestHandler)
  119. );
  120. return http.build();
  121. }
  122. ----
  123. .Kotlin
  124. [source,kotlin,role="secondary"]
  125. ----
  126. @Bean
  127. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  128. val requestHandler = CsrfTokenRequestAttributeHandler()
  129. // set the name of the attribute the CsrfToken will be populated on
  130. requestHandler.setCsrfRequestAttributeName("_csrf")
  131. http {
  132. csrf {
  133. csrfTokenRequestHandler = requestHandler
  134. }
  135. }
  136. return http.build()
  137. }
  138. ----
  139. .XML
  140. [source,xml,role="secondary"]
  141. ----
  142. <http>
  143. <!-- ... -->
  144. <csrf request-handler-ref="requestHandler"/>
  145. </http>
  146. <b:bean id="requestHandler"
  147. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
  148. p:csrfRequestAttributeName="_csrf"/>
  149. ----
  150. ====
  151. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  152. .Explicit Configure `CsrfToken` with 5.8 Defaults
  153. ====
  154. .Java
  155. [source,java,role="primary"]
  156. ----
  157. @Bean
  158. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  159. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  160. // set the name of the attribute the CsrfToken will be populated on
  161. requestHandler.setCsrfRequestAttributeName(null);
  162. http
  163. // ...
  164. .csrf((csrf) -> csrf
  165. .csrfTokenRequestHandler(requestHandler)
  166. );
  167. return http.build();
  168. }
  169. ----
  170. .Kotlin
  171. [source,kotlin,role="secondary"]
  172. ----
  173. @Bean
  174. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  175. val requestHandler = CsrfTokenRequestAttributeHandler()
  176. // set the name of the attribute the CsrfToken will be populated on
  177. requestHandler.setCsrfRequestAttributeName(null)
  178. http {
  179. csrf {
  180. csrfTokenRequestHandler = requestHandler
  181. }
  182. }
  183. return http.build()
  184. }
  185. ----
  186. .XML
  187. [source,xml,role="secondary"]
  188. ----
  189. <http>
  190. <!-- ... -->
  191. <csrf request-handler-ref="requestHandler"/>
  192. </http>
  193. <b:bean id="requestHandler"
  194. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
  195. <b:property name="csrfRequestAttributeName">
  196. <b:null/>
  197. </b:property>
  198. </b:bean>
  199. ----
  200. ====
  201. === CSRF BREACH Protection
  202. 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:
  203. .`CsrfToken` BREACH Protection
  204. ====
  205. .Java
  206. [source,java,role="primary"]
  207. ----
  208. @Bean
  209. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  210. XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
  211. // set the name of the attribute the CsrfToken will be populated on
  212. requestHandler.setCsrfRequestAttributeName("_csrf");
  213. http
  214. // ...
  215. .csrf((csrf) -> csrf
  216. .csrfTokenRequestHandler(requestHandler)
  217. );
  218. return http.build();
  219. }
  220. ----
  221. .Kotlin
  222. [source,kotlin,role="secondary"]
  223. ----
  224. @Bean
  225. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  226. val requestHandler = XorCsrfTokenRequestAttributeHandler()
  227. // set the name of the attribute the CsrfToken will be populated on
  228. requestHandler.setCsrfRequestAttributeName("_csrf")
  229. http {
  230. csrf {
  231. csrfTokenRequestHandler = requestHandler
  232. }
  233. }
  234. return http.build()
  235. }
  236. ----
  237. .XML
  238. [source,xml,role="secondary"]
  239. ----
  240. <http>
  241. <!-- ... -->
  242. <csrf request-handler-ref="requestHandler"/>
  243. </http>
  244. <b:bean id="requestHandler"
  245. class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
  246. p:csrfRequestAttributeName="_csrf"/>
  247. ----
  248. ====
  249. === Explicit Save SecurityContextRepository
  250. 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`].
  251. Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
  252. 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`).
  253. 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.
  254. 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`.
  255. Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
  256. This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
  257. To opt into the new Spring Security 6 default, the following configuration can be used.
  258. include::partial$servlet/architecture/security-context-explicit.adoc[]
  259. === Multiple SecurityContextRepository
  260. In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] is `HttpSessionSecurityContextRepository`.
  261. In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
  262. To opt into the new Spring Security 6 default, the following configuration can be used.
  263. .Configure SecurityContextRepository with 6.0 defaults
  264. ====
  265. .Java
  266. [source,java,role="primary"]
  267. ----
  268. @Bean
  269. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  270. http
  271. // ...
  272. .securityContext((securityContext) -> securityContext
  273. .securityContextRepository(new DelegatingSecurityContextRepository(
  274. new RequestAttributeSecurityContextRepository(),
  275. new HttpSessionSecurityContextRepository()
  276. ))
  277. );
  278. return http.build();
  279. }
  280. ----
  281. .Kotlin
  282. [source,kotlin,role="secondary"]
  283. ----
  284. @Bean
  285. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  286. http {
  287. // ...
  288. securityContext {
  289. securityContextRepository = DelegatingSecurityContextRepository(
  290. RequestAttributeSecurityContextRepository(),
  291. HttpSessionSecurityContextRepository()
  292. )
  293. }
  294. }
  295. return http.build()
  296. }
  297. ----
  298. .XML
  299. [source,xml,role="secondary"]
  300. ----
  301. <http security-context-repository-ref="contextRepository">
  302. <!-- ... -->
  303. </http>
  304. <bean name="contextRepository"
  305. class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
  306. <constructor-arg>
  307. <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  308. </constructor-arg>
  309. <constructor-arg>
  310. <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
  311. </constructor-arg>
  312. </bean>
  313. ----
  314. ====
  315. [IMPORTANT]
  316. ====
  317. 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`.
  318. ====
  319. === Deprecation in SecurityContextRepository
  320. In Spring Security 5.7, a new method was added to xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] with the signature:
  321. Supplier<SecurityContext> loadContext(HttpServletRequest request)
  322. 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:
  323. DeferredSecurityContext loadDeferredContext(HttpServletRequest request)
  324. In Spring Security 6, the deprecated method was removed.
  325. 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.
  326. To get started implementing the new method, use the following example to provide a `DeferredSecurityContext`:
  327. .Provide `DeferredSecurityContext`
  328. ====
  329. .Java
  330. [source,java,role="primary"]
  331. ----
  332. @Override
  333. public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
  334. return new DeferredSecurityContext() {
  335. private SecurityContext securityContext;
  336. private boolean isGenerated;
  337. @Override
  338. public SecurityContext get() {
  339. if (this.securityContext == null) {
  340. this.securityContext = getContextOrNull(request);
  341. if (this.securityContext == null) {
  342. SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
  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. return object : DeferredSecurityContext {
  362. private var securityContext: SecurityContext? = null
  363. private var isGenerated = false
  364. override fun get(): SecurityContext {
  365. if (securityContext == null) {
  366. securityContext = getContextOrNull(request)
  367. ?: SecurityContextHolder.getContextHolderStrategy().createEmptyContext()
  368. .also { isGenerated = true }
  369. }
  370. return securityContext!!
  371. }
  372. override fun isGenerated(): Boolean {
  373. get()
  374. return isGenerated
  375. }
  376. }
  377. }
  378. ----
  379. ====
  380. [[requestcache-query-optimization]]
  381. === Optimize Querying of `RequestCache`
  382. In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
  383. 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.
  384. In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
  385. This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
  386. In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
  387. 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:
  388. include::partial$servlet/architecture/request-cache-continue.adoc[]
  389. === Use `AuthorizationManager` for Method Security
  390. 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.
  391. 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.
  392. [[servlet-replace-globalmethodsecurity-with-methodsecurity]]
  393. ==== 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]
  394. {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.
  395. 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.
  396. This means that the following two listings are functionally equivalent:
  397. ====
  398. .Java
  399. [source,java,role="primary"]
  400. ----
  401. @EnableGlobalMethodSecurity(prePostEnabled = true)
  402. ----
  403. .Kotlin
  404. [source,kotlin,role="secondary"]
  405. ----
  406. @EnableGlobalMethodSecurity(prePostEnabled = true)
  407. ----
  408. .Xml
  409. [source,xml,role="secondary"]
  410. ----
  411. <global-method-security pre-post-enabled="true"/>
  412. ----
  413. ====
  414. and:
  415. ====
  416. .Java
  417. [source,java,role="primary"]
  418. ----
  419. @EnableMethodSecurity
  420. ----
  421. .Kotlin
  422. [source,kotlin,role="secondary"]
  423. ----
  424. @EnableMethodSecurity
  425. ----
  426. .Xml
  427. [source,xml,role="secondary"]
  428. ----
  429. <method-security/>
  430. ----
  431. ====
  432. For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.
  433. For example, a listing like:
  434. ====
  435. .Java
  436. [source,java,role="primary"]
  437. ----
  438. @EnableGlobalMethodSecurity(securedEnabled = true)
  439. ----
  440. .Kotlin
  441. [source,kotlin,role="secondary"]
  442. ----
  443. @EnableGlobalMethodSecurity(securedEnabled = true)
  444. ----
  445. .Xml
  446. [source,xml,role="secondary"]
  447. ----
  448. <global-method-security secured-enabled="true"/>
  449. ----
  450. ====
  451. should change to:
  452. ====
  453. .Java
  454. [source,java,role="primary"]
  455. ----
  456. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  457. ----
  458. .Kotlin
  459. [source,kotlin,role="secondary"]
  460. ----
  461. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  462. ----
  463. .Xml
  464. [source,xml,role="secondary"]
  465. ----
  466. <method-security secured-enabled="true" pre-post-enabled="false"/>
  467. ----
  468. ====
  469. [[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
  470. ==== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
  471. `@EnableMethodSecurity` does not pick up a `PermissionEvaluator`.
  472. This helps keep its API simple.
  473. If you have a custom {security-api-url}org/springframework/security/access/PermissionEvaluator.html[`PermissionEvaluator`] `@Bean`, please change it from:
  474. ====
  475. .Java
  476. [source,java,role="primary"]
  477. ----
  478. @Bean
  479. static PermissionEvaluator permissionEvaluator() {
  480. // ... your evaluator
  481. }
  482. ----
  483. .Kotlin
  484. [source,kotlin,role="secondary"]
  485. ----
  486. companion object {
  487. @Bean
  488. fun permissionEvaluator(): PermissionEvaluator {
  489. // ... your evaluator
  490. }
  491. }
  492. ----
  493. ====
  494. to:
  495. ====
  496. .Java
  497. [source,java,role="primary"]
  498. ----
  499. @Bean
  500. static MethodSecurityExpressionHandler expressionHandler() {
  501. var expressionHandler = new DefaultMethodSecurityExpressionHandler();
  502. expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
  503. return expressionHandler;
  504. }
  505. ----
  506. .Kotlin
  507. [source,kotlin,role="secondary"]
  508. ----
  509. companion object {
  510. @Bean
  511. fun expressionHandler(): MethodSecurityExpressionHandler {
  512. val expressionHandler = DefaultMethodSecurityExpressionHandler
  513. expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
  514. return expressionHandler
  515. }
  516. }
  517. ----
  518. ====
  519. ==== Replace any custom method-security ``AccessDecisionManager``s
  520. 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.
  521. The preparation strategy will depend on your reason for each arrangement.
  522. Read on to find the best match for your situation.
  523. ===== I use `UnanimousBased`
  524. 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`].
  525. However, if you do discover that you cannot accept the default authorization managers, you can use `AuthorizationManagers.allOf` to compose your own arrangement.
  526. 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`].
  527. ===== I use `AffirmativeBased`
  528. 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:
  529. ====
  530. .Java
  531. [source,java,role="primary"]
  532. ----
  533. AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
  534. // ... your list of authorization managers
  535. )
  536. ----
  537. .Kotlin
  538. [source,kotlin,role="secondary"]
  539. ----
  540. val authorization = AuthorizationManagers.anyOf(
  541. // ... your list of authorization managers
  542. )
  543. ----
  544. ====
  545. 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`].
  546. ===== I use `ConsensusBased`
  547. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  548. 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.
  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 a custom `AccessDecisionVoter`
  551. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  552. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  553. 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:
  554. ====
  555. .Java
  556. [source,java,role="primary"]
  557. ----
  558. public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
  559. private final SecurityMetadataSource metadata;
  560. private final AccessDecisionVoter voter;
  561. public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
  562. ExpressionBasedAnnotationAttributeFactory attributeFactory =
  563. new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
  564. this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
  565. ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
  566. expressionAdvice.setExpressionHandler(expressionHandler);
  567. this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
  568. }
  569. public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
  570. List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
  571. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  572. if (decision == ACCESS_GRANTED) {
  573. return new AuthorizationDecision(true);
  574. }
  575. if (decision == ACCESS_DENIED) {
  576. return new AuthorizationDecision(false);
  577. }
  578. return null; // abstain
  579. }
  580. }
  581. ----
  582. ====
  583. 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`].
  584. ===== I use a custom `AfterInvocationManager`
  585. {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`].
  586. The difference is that `AuthorizationManager<MethodInvocation>` replaces `AccessDecisionManager` and `AuthorizationManager<MethodInvocationResult>` replaces `AfterInvocationManager`.
  587. 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`.
  588. ===== I use `RunAsManager`
  589. There is currently https://github.com/spring-projects/spring-security/issues/11331[no replacement for `RunAsManager`] though one is being considered.
  590. It is quite straightforward to adapt a `RunAsManager`, though, to the `AuthorizationManager` API, if needed.
  591. Here is some pseudocode to get you started:
  592. ====
  593. .Java
  594. [source,java,role="primary"]
  595. ----
  596. public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
  597. private final RunAsManager runAs = new RunAsManagerImpl();
  598. private final SecurityMetadataSource metadata;
  599. private final AuthorizationManager<T> authorization;
  600. // ... constructor
  601. public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
  602. Supplier<Authentication> wrapped = (auth) -> {
  603. List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
  604. return this.runAs.buildRunAs(auth, object, attributes);
  605. };
  606. return this.authorization.check(wrapped, object);
  607. }
  608. }
  609. ----
  610. ====
  611. 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`].
  612. [[servlet-check-for-annotationconfigurationexceptions]]
  613. ==== Check for ``AnnotationConfigurationException``s
  614. `@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  615. 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.
  616. === Use `AuthorizationManager` for Message Security
  617. 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.
  618. 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.
  619. ==== Ensure all messages have defined authorization rules
  620. The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
  621. xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
  622. To prepare for this, ensure that authorization rules exist are declared for every request.
  623. For example, an application configuration like:
  624. ====
  625. .Java
  626. [source,java,role="primary"]
  627. ----
  628. @Override
  629. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  630. messages
  631. .simpDestMatchers("/user/queue/errors").permitAll()
  632. .simpDestMatchers("/admin/**").hasRole("ADMIN");
  633. }
  634. ----
  635. .Kotlin
  636. [source,kotlin,role="secondary"]
  637. ----
  638. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  639. messages
  640. .simpDestMatchers("/user/queue/errors").permitAll()
  641. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  642. }
  643. ----
  644. .Xml
  645. [source,xml,role="secondary"]
  646. ----
  647. <websocket-message-broker>
  648. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  649. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  650. </websocket-message-broker>
  651. ----
  652. ====
  653. should change to:
  654. ====
  655. .Java
  656. [source,java,role="primary"]
  657. ----
  658. @Override
  659. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  660. messages
  661. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  662. .simpDestMatchers("/user/queue/errors").permitAll()
  663. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  664. .anyMessage().denyAll();
  665. }
  666. ----
  667. .Kotlin
  668. [source,kotlin,role="secondary"]
  669. ----
  670. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  671. messages
  672. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  673. .simpDestMatchers("/user/queue/errors").permitAll()
  674. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  675. .anyMessage().denyAll()
  676. }
  677. ----
  678. .Xml
  679. [source,xml,role="secondary"]
  680. ----
  681. <websocket-message-broker>
  682. <intercept-message type="CONNECT" access="permitAll"/>
  683. <intercept-message type="DISCONNECT" access="permitAll"/>
  684. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  685. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  686. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  687. <intercept-message pattern="/**" access="denyAll"/>
  688. </websocket-message-broker>
  689. ----
  690. ====
  691. ==== Add `@EnableWebSocketSecurity`
  692. [NOTE]
  693. ====
  694. If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
  695. Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
  696. Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
  697. ====
  698. If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
  699. For example, you can add it to your websocket security configuration class, like so:
  700. ====
  701. .Java
  702. [source,java,role="primary"]
  703. ----
  704. @EnableWebSocketSecurity
  705. @Configuration
  706. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  707. // ...
  708. }
  709. ----
  710. .Kotlin
  711. [source,kotlin,role="secondary"]
  712. ----
  713. @EnableWebSocketSecurity
  714. @Configuration
  715. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  716. // ...
  717. }
  718. ----
  719. ====
  720. This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
  721. ==== Use an `AuthorizationManager<Message<?>>` instance
  722. To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
  723. For example, the following application configuration:
  724. ====
  725. .Java
  726. [source,java,role="primary"]
  727. ----
  728. @Override
  729. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  730. messages
  731. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  732. .simpDestMatchers("/user/queue/errors").permitAll()
  733. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  734. .anyMessage().denyAll();
  735. }
  736. ----
  737. .Kotlin
  738. [source,kotlin,role="secondary"]
  739. ----
  740. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  741. messages
  742. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  743. .simpDestMatchers("/user/queue/errors").permitAll()
  744. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  745. .anyMessage().denyAll()
  746. }
  747. ----
  748. .Xml
  749. [source,xml,role="secondary"]
  750. ----
  751. <websocket-message-broker>
  752. <intercept-message type="CONNECT" access="permitAll"/>
  753. <intercept-message type="DISCONNECT" access="permitAll"/>
  754. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  755. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  756. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  757. <intercept-message pattern="/**" access="denyAll"/>
  758. </websocket-message-broker>
  759. ----
  760. ====
  761. changes to:
  762. ====
  763. .Java
  764. [source,java,role="primary"]
  765. ----
  766. @Bean
  767. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  768. messages
  769. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  770. .simpDestMatchers("/user/queue/errors").permitAll()
  771. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  772. .anyMessage().denyAll();
  773. return messages.build();
  774. }
  775. ----
  776. .Kotlin
  777. [source,kotlin,role="secondary"]
  778. ----
  779. @Bean
  780. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  781. messages
  782. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  783. .simpDestMatchers("/user/queue/errors").permitAll()
  784. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  785. .anyMessage().denyAll()
  786. return messages.build()
  787. }
  788. ----
  789. .Xml
  790. [source,xml,role="secondary"]
  791. ----
  792. <websocket-message-broker use-authorization-manager="true">
  793. <intercept-message type="CONNECT" access="permitAll"/>
  794. <intercept-message type="DISCONNECT" access="permitAll"/>
  795. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  796. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  797. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  798. <intercept-message pattern="/**" access="denyAll"/>
  799. </websocket-message-broker>
  800. ----
  801. ====
  802. ==== Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
  803. If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
  804. For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
  805. ====
  806. .Java
  807. [source,java,role="primary"]
  808. ----
  809. @EnableWebSocketSecurity
  810. @Configuration
  811. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  812. // ...
  813. }
  814. ----
  815. .Kotlin
  816. [source,kotlin,role="secondary"]
  817. ----
  818. @EnableWebSocketSecurity
  819. @Configuration
  820. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  821. // ...
  822. }
  823. ----
  824. ====
  825. changes to:
  826. ====
  827. .Java
  828. [source,java,role="primary"]
  829. ----
  830. @EnableWebSocketSecurity
  831. @Configuration
  832. public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
  833. // ...
  834. }
  835. ----
  836. .Kotlin
  837. [source,kotlin,role="secondary"]
  838. ----
  839. @EnableWebSocketSecurity
  840. @Configuration
  841. class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
  842. // ...
  843. }
  844. ----
  845. ====
  846. [[servlet-authorizationmanager-messages-opt-out]]
  847. ==== Opt-out Steps
  848. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  849. ===== I cannot declare an authorization rule for all requests
  850. 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:
  851. ====
  852. .Java
  853. [source,java,role="primary"]
  854. ----
  855. @Bean
  856. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  857. messages
  858. .simpDestMatchers("/user/queue/errors").permitAll()
  859. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  860. // ...
  861. .anyMessage().permitAll();
  862. return messages.build();
  863. }
  864. ----
  865. .Kotlin
  866. [source,kotlin,role="secondary"]
  867. ----
  868. @Bean
  869. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  870. messages
  871. .simpDestMatchers("/user/queue/errors").permitAll()
  872. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  873. // ...
  874. .anyMessage().permitAll();
  875. return messages.build()
  876. }
  877. ----
  878. .Xml
  879. [source,xml,role="secondary"]
  880. ----
  881. <websocket-message-broker use-authorization-manager="true">
  882. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  883. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  884. <!-- ... -->
  885. <intercept-message pattern="/**" access="permitAll"/>
  886. </websocket-message-broker>
  887. ----
  888. ====
  889. ===== I cannot get CSRF working, need some other `AbstractSecurityWebSocketMessageBrokerConfigurer` feature, or am having trouble with `AuthorizationManager`
  890. In the case of Java, you may continue using `AbstractMessageSecurityWebSocketMessageBrokerConfigurer`.
  891. Even though it is deprecated, it will not be removed in 6.0.
  892. In the case of XML, you can opt out of `AuthorizationManager` by setting `use-authorization-manager="false"`:
  893. ====
  894. .Xml
  895. [source,xml,role="secondary"]
  896. ----
  897. <websocket-message-broker>
  898. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  899. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  900. </websocket-message-broker>
  901. ----
  902. ====
  903. to:
  904. ====
  905. .Xml
  906. [source,xml,role="secondary"]
  907. ----
  908. <websocket-message-broker use-authorization-manager="false">
  909. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  910. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  911. </websocket-message-broker>
  912. ----
  913. ====
  914. === Use `AuthorizationManager` for Request Security
  915. 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].
  916. 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.
  917. ==== Ensure that all requests have defined authorization rules
  918. In Spring Security 5.8 and earlier, requests with no authorization rule are permitted by default.
  919. It is a stronger security position to deny by default, thus requiring that authorization rules be clearly defined for every endpoint.
  920. As such, in 6.0, Spring Security by default denies any request that is missing an authorization rule.
  921. 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.
  922. 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.
  923. [NOTE]
  924. ====
  925. You may already have an `anyRequest` rule defined that you are happy with in which case this step can be skipped.
  926. ====
  927. Adding `denyAll` to the end looks like changing:
  928. ====
  929. .Java
  930. [source,java,role="primary"]
  931. ----
  932. http
  933. .authorizeRequests((authorize) -> authorize
  934. .filterSecurityInterceptorOncePerRequest(true)
  935. .mvcMatchers("/app/**").hasRole("APP")
  936. // ...
  937. )
  938. // ...
  939. ----
  940. .Kotlin
  941. [source,kotlin,role="secondary"]
  942. ----
  943. http {
  944. authorizeRequests {
  945. filterSecurityInterceptorOncePerRequest = true
  946. authorize("/app/**", hasRole("APP"))
  947. // ...
  948. }
  949. }
  950. ----
  951. .Xml
  952. [source,xml,role="secondary"]
  953. ----
  954. <http once-per-request="true">
  955. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  956. <!-- ... -->
  957. </http>
  958. ----
  959. ====
  960. to:
  961. ====
  962. .Java
  963. [source,java,role="primary"]
  964. ----
  965. http
  966. .authorizeRequests((authorize) -> authorize
  967. .filterSecurityInterceptorOncePerRequest(true)
  968. .mvcMatchers("/app/**").hasRole("APP")
  969. // ...
  970. .anyRequest().denyAll()
  971. )
  972. // ...
  973. ----
  974. .Kotlin
  975. [source,kotlin,role="secondary"]
  976. ----
  977. http {
  978. authorizeRequests {
  979. filterSecurityInterceptorOncePerRequest = true
  980. authorize("/app/**", hasRole("APP"))
  981. // ...
  982. authorize(anyRequest, denyAll)
  983. }
  984. }
  985. ----
  986. .Xml
  987. [source,xml,role="secondary"]
  988. ----
  989. <http once-per-request="true">
  990. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  991. <!-- ... -->
  992. <intercept-url pattern="/**" access="denyAll"/>
  993. </http>
  994. ----
  995. ====
  996. If you have already migrated to `authorizeHttpRequests`, the recommended change is the same.
  997. ==== Switch to `AuthorizationManager`
  998. 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.
  999. Change:
  1000. ====
  1001. .Java
  1002. [source,java,role="primary"]
  1003. ----
  1004. http
  1005. .authorizeRequests((authorize) -> authorize
  1006. .filterSecurityInterceptorOncePerRequest(true)
  1007. .mvcMatchers("/app/**").hasRole("APP")
  1008. // ...
  1009. .anyRequest().denyAll()
  1010. )
  1011. // ...
  1012. ----
  1013. .Kotlin
  1014. [source,kotlin,role="secondary"]
  1015. ----
  1016. http {
  1017. authorizeRequests {
  1018. filterSecurityInterceptorOncePerRequest = true
  1019. authorize("/app/**", hasRole("APP"))
  1020. // ...
  1021. authorize(anyRequest, denyAll)
  1022. }
  1023. }
  1024. ----
  1025. .Xml
  1026. [source,xml,role="secondary"]
  1027. ----
  1028. <http once-per-request="true">
  1029. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1030. <!-- ... -->
  1031. <intercept-url pattern="/**" access="denyAll"/>
  1032. </http>
  1033. ----
  1034. ====
  1035. to:
  1036. ====
  1037. .Java
  1038. [source,java,role="primary"]
  1039. ----
  1040. http
  1041. .authorizeHttpRequests((authorize) -> authorize
  1042. .shouldFilterAllDispatcherTypes(false)
  1043. .mvcMatchers("/app/**").hasRole("APP")
  1044. // ...
  1045. .anyRequest().denyAll()
  1046. )
  1047. // ...
  1048. ----
  1049. .Kotlin
  1050. [source,kotlin,role="secondary"]
  1051. ----
  1052. http {
  1053. authorizeHttpRequests {
  1054. shouldFilterAllDispatcherTypes = false
  1055. authorize("/app/**", hasRole("APP"))
  1056. // ...
  1057. authorize(anyRequest, denyAll)
  1058. }
  1059. }
  1060. ----
  1061. .Xml
  1062. [source,xml,role="secondary"]
  1063. ----
  1064. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1065. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1066. <!-- ... -->
  1067. <intercept-url pattern="/**" access="denyAll"/>
  1068. </http>
  1069. ----
  1070. ====
  1071. ==== Migrate SpEL expressions to `AuthorizationManager`
  1072. For authorization rules, Java tends to be easier to test and maintain than SpEL.
  1073. As such, `authorizeHttpRequests` does not have a method for declaring a `String` SpEL.
  1074. Instead, you can implement your own `AuthorizationManager` implementation or use `WebExpressionAuthorizationManager`.
  1075. For completeness, both options will be demonstrated.
  1076. First, if you have the following SpEL:
  1077. ====
  1078. .Java
  1079. [source,java,role="primary"]
  1080. ----
  1081. http
  1082. .authorizeRequests((authorize) -> authorize
  1083. .filterSecurityInterceptorOncePerRequest(true)
  1084. .mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  1085. // ...
  1086. .anyRequest().denyAll()
  1087. )
  1088. // ...
  1089. ----
  1090. .Kotlin
  1091. [source,kotlin,role="secondary"]
  1092. ----
  1093. http {
  1094. authorizeRequests {
  1095. filterSecurityInterceptorOncePerRequest = true
  1096. authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  1097. // ...
  1098. authorize(anyRequest, denyAll)
  1099. }
  1100. }
  1101. ----
  1102. ====
  1103. Then you can compose your own `AuthorizationManager` with Spring Security authorization primitives like so:
  1104. ====
  1105. .Java
  1106. [source,java,role="primary"]
  1107. ----
  1108. http
  1109. .authorizeHttpRequests((authorize) -> authorize
  1110. .shouldFilterAllDispatcherTypes(false)
  1111. .mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  1112. // ...
  1113. .anyRequest().denyAll()
  1114. )
  1115. // ...
  1116. ----
  1117. .Kotlin
  1118. [source,kotlin,role="secondary"]
  1119. ----
  1120. http {
  1121. authorizeHttpRequests {
  1122. shouldFilterAllDispatcherTypes = false
  1123. authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  1124. // ...
  1125. authorize(anyRequest, denyAll)
  1126. }
  1127. }
  1128. ----
  1129. ====
  1130. Or you can use `WebExpressionAuthorizationManager` in the following way:
  1131. ====
  1132. .Java
  1133. [source,java,role="primary"]
  1134. ----
  1135. http
  1136. .authorizeRequests((authorize) -> authorize
  1137. .filterSecurityInterceptorOncePerRequest(true)
  1138. .mvcMatchers("/complicated/**").access(
  1139. new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  1140. )
  1141. // ...
  1142. .anyRequest().denyAll()
  1143. )
  1144. // ...
  1145. ----
  1146. .Kotlin
  1147. [source,kotlin,role="secondary"]
  1148. ----
  1149. http {
  1150. authorizeRequests {
  1151. filterSecurityInterceptorOncePerRequest = true
  1152. authorize("/complicated/**", access(
  1153. WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  1154. )
  1155. // ...
  1156. authorize(anyRequest, denyAll)
  1157. }
  1158. }
  1159. ----
  1160. ====
  1161. [[switch-filter-all-dispatcher-types]]
  1162. ==== Switch to filter all dispatcher types
  1163. Spring Security 5.8 and earlier only xref:servlet/authorization/architecture.adoc[perform authorization] once per request.
  1164. This means that dispatcher types like `FORWARD` and `INCLUDE` that run after `REQUEST` are not secured by default.
  1165. It's recommended that Spring Security secure all dispatch types.
  1166. As such, in 6.0, Spring Security changes this default.
  1167. So, finally, change your authorization rules to filter all dispatcher types.
  1168. To do this, you should change:
  1169. ====
  1170. .Java
  1171. [source,java,role="primary"]
  1172. ----
  1173. http
  1174. .authorizeHttpRequests((authorize) -> authorize
  1175. .shouldFilterAllDispatcherTypes(false)
  1176. .mvcMatchers("/app/**").hasRole("APP")
  1177. // ...
  1178. .anyRequest().denyAll()
  1179. )
  1180. // ...
  1181. ----
  1182. .Kotlin
  1183. [source,kotlin,role="secondary"]
  1184. ----
  1185. http {
  1186. authorizeHttpRequests {
  1187. shouldFilterAllDispatcherTypes = false
  1188. authorize("/app/**", hasRole("APP"))
  1189. // ...
  1190. authorize(anyRequest, denyAll)
  1191. }
  1192. }
  1193. ----
  1194. .Xml
  1195. [source,xml,role="secondary"]
  1196. ----
  1197. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1198. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1199. <!-- ... -->
  1200. <intercept-url pattern="/**" access="denyAll"/>
  1201. </http>
  1202. ----
  1203. ====
  1204. to:
  1205. ====
  1206. .Java
  1207. [source,java,role="primary"]
  1208. ----
  1209. http
  1210. .authorizeHttpRequests((authorize) -> authorize
  1211. .shouldFilterAllDispatcherTypes(true)
  1212. .mvcMatchers("/app/**").hasRole("APP")
  1213. // ...
  1214. .anyRequest().denyAll()
  1215. )
  1216. // ...
  1217. ----
  1218. .Kotlin
  1219. [source,kotlin,role="secondary"]
  1220. ----
  1221. http {
  1222. authorizeHttpRequests {
  1223. shouldFilterAllDispatcherTypes = true
  1224. authorize("/app/**", hasRole("APP"))
  1225. // ...
  1226. authorize(anyRequest, denyAll)
  1227. }
  1228. }
  1229. ----
  1230. .Xml
  1231. [source,xml,role="secondary"]
  1232. ----
  1233. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1234. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1235. <!-- ... -->
  1236. <intercept-url pattern="/**" access="denyAll"/>
  1237. </http>
  1238. ----
  1239. ====
  1240. And, the `FilterChainProxy` should be registered for all dispatcher types as well.
  1241. If you are using Spring Boot, https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.security.spring.security.filter.dispatcher-types[you have to change the `spring.security.filter.dispatcher-types` property] to include all dispatcher types:
  1242. ====
  1243. .application.properties
  1244. [source,properties,role="primary"]
  1245. ----
  1246. spring.security.filter.dispatcher-types=request,async,error,forward,include
  1247. ----
  1248. ====
  1249. If you are xref::servlet/configuration/java.adoc#_abstractsecuritywebapplicationinitializer[using the `AbstractSecurityWebApplicationInitializer`] you should override the `getSecurityDispatcherTypes` method and return all dispatcher types:
  1250. ====
  1251. .Java
  1252. [source,java,role="primary"]
  1253. ----
  1254. import org.springframework.security.web.context.*;
  1255. public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
  1256. @Override
  1257. protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
  1258. return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.FORWARD,
  1259. DispatcherType.FORWARD, DispatcherType.INCLUDE);
  1260. }
  1261. }
  1262. ----
  1263. ====
  1264. ===== Permit `FORWARD` when using Spring MVC
  1265. If you are using {spring-framework-reference-url}/web.html#mvc-viewresolver[Spring MVC to resolve view names], you will need to permit `FORWARD` requests.
  1266. This is because when Spring MVC detects a mapping between view name and the actual views, it will perform a forward to the view.
  1267. As we saw on the <<switch-filter-all-dispatcher-types,previous section>>, Spring Security 6.0 will apply authorization to `FORWARD` requests by default.
  1268. Consider the following common configuration:
  1269. ====
  1270. .Java
  1271. [source,java,role="primary"]
  1272. ----
  1273. @Bean
  1274. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  1275. http
  1276. .authorizeHttpRequests((authorize) -> authorize
  1277. .shouldFilterAllDispatcherTypes(true)
  1278. .requestMatchers("/").authenticated()
  1279. .anyRequest().denyAll()
  1280. )
  1281. .formLogin((form) -> form
  1282. .loginPage("/login")
  1283. .permitAll()
  1284. ));
  1285. return http.build();
  1286. }
  1287. ----
  1288. ====
  1289. and one of the following equivalents MVC view mapping configurations:
  1290. ====
  1291. .Java
  1292. [source,java,role="primary"]
  1293. ----
  1294. @Controller
  1295. public class MyController {
  1296. @GetMapping("/login")
  1297. public String login() {
  1298. return "login";
  1299. }
  1300. }
  1301. ----
  1302. ====
  1303. ====
  1304. .Java
  1305. [source,java,role="primary"]
  1306. ----
  1307. @Configuration
  1308. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  1309. @Override
  1310. public void addViewControllers(ViewControllerRegistry registry) {
  1311. registry.addViewController("/login").setViewName("login");
  1312. }
  1313. }
  1314. ----
  1315. ====
  1316. With either configuration, when there is a request to `/login`, Spring MVC will perform a *forward* to the view `login`, which, with the default configuration, is under `src/main/resources/templates/login.html` path.
  1317. The security configuration permits requests to `/login` but every other request will be denied, including the `FORWARD` request to the view under `/templates/login.html`.
  1318. To fix this, you should configure Spring Security to permit `FORWARD` requests:
  1319. ====
  1320. .Java
  1321. [source,java,role="primary"]
  1322. ----
  1323. http
  1324. .authorizeHttpRequests((authorize) -> authorize
  1325. .shouldFilterAllDispatcherTypes(true)
  1326. .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
  1327. .anyRequest().denyAll()
  1328. )
  1329. // ...
  1330. ----
  1331. .Kotlin
  1332. [source,kotlin,role="secondary"]
  1333. ----
  1334. http {
  1335. authorizeHttpRequests {
  1336. shouldFilterAllDispatcherTypes = true
  1337. authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
  1338. authorize(anyRequest, denyAll)
  1339. }
  1340. }
  1341. ----
  1342. .Xml
  1343. [source,xml,role="secondary"]
  1344. ----
  1345. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1346. <intercept-url request-matcher-ref="forwardRequestMatcher" access="permitAll()" />
  1347. <!-- ... -->
  1348. <intercept-url pattern="/**" access="denyAll"/>
  1349. </http>
  1350. <bean name="forwardRequestMatcher" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  1351. <constructor-arg value="FORWARD"/>
  1352. </bean>
  1353. ----
  1354. ====
  1355. ==== Replace any custom filter-security ``AccessDecisionManager``s
  1356. 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.
  1357. The preparation strategy will depend on your reason for each arrangement.
  1358. Read on to find the best match for your situation.
  1359. ===== I use `UnanimousBased`
  1360. 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:
  1361. ====
  1362. .Java
  1363. [source,java,role="primary"]
  1364. ----
  1365. @Bean
  1366. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  1367. PolicyAuthorizationManager policy = ...;
  1368. LocalAuthorizationManager local = ...;
  1369. return AuthorizationMangers.allOf(policy, local);
  1370. }
  1371. ----
  1372. .Kotlin
  1373. [source,kotlin,role="secondary"]
  1374. ----
  1375. @Bean
  1376. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  1377. val policy: PolicyAuthorizationManager = ...
  1378. val local: LocalAuthorizationManager = ...
  1379. return AuthorizationMangers.allOf(policy, local)
  1380. }
  1381. ----
  1382. .Xml
  1383. [source,xml,role="secondary"]
  1384. ----
  1385. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1386. factory-method="allOf">
  1387. <constructor-arg>
  1388. <util:list>
  1389. <bean class="my.PolicyAuthorizationManager"/>
  1390. <bean class="my.LocalAuthorizationManager"/>
  1391. </util:list>
  1392. </constructor-arg>
  1393. </bean>
  1394. ----
  1395. ====
  1396. then, wire it into the DSL like so:
  1397. ====
  1398. .Java
  1399. [source,java,role="primary"]
  1400. ----
  1401. http
  1402. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1403. // ...
  1404. ----
  1405. .Kotlin
  1406. [source,kotlin,role="secondary"]
  1407. ----
  1408. http {
  1409. authorizeHttpRequests {
  1410. authorize(anyRequest, requestAuthorization)
  1411. }
  1412. // ...
  1413. }
  1414. ----
  1415. .Xml
  1416. [source,xml,role="secondary"]
  1417. ----
  1418. <http authorization-manager-ref="requestAuthorization"/>
  1419. ----
  1420. ====
  1421. [NOTE]
  1422. ====
  1423. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1424. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1425. ====
  1426. ===== I use `AffirmativeBased`
  1427. 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:
  1428. ====
  1429. .Java
  1430. [source,java,role="primary"]
  1431. ----
  1432. @Bean
  1433. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  1434. PolicyAuthorizationManager policy = ...;
  1435. LocalAuthorizationManager local = ...;
  1436. return AuthorizationMangers.anyOf(policy, local);
  1437. }
  1438. ----
  1439. .Kotlin
  1440. [source,kotlin,role="secondary"]
  1441. ----
  1442. @Bean
  1443. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  1444. val policy: PolicyAuthorizationManager = ...
  1445. val local: LocalAuthorizationManager = ...
  1446. return AuthorizationMangers.anyOf(policy, local)
  1447. }
  1448. ----
  1449. .Xml
  1450. [source,xml,role="secondary"]
  1451. ----
  1452. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1453. factory-method="anyOf">
  1454. <constructor-arg>
  1455. <util:list>
  1456. <bean class="my.PolicyAuthorizationManager"/>
  1457. <bean class="my.LocalAuthorizationManager"/>
  1458. </util:list>
  1459. </constructor-arg>
  1460. </bean>
  1461. ----
  1462. ====
  1463. then, wire it into the DSL like so:
  1464. ====
  1465. .Java
  1466. [source,java,role="primary"]
  1467. ----
  1468. http
  1469. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1470. // ...
  1471. ----
  1472. .Kotlin
  1473. [source,kotlin,role="secondary"]
  1474. ----
  1475. http {
  1476. authorizeHttpRequests {
  1477. authorize(anyRequest, requestAuthorization)
  1478. }
  1479. // ...
  1480. }
  1481. ----
  1482. .Xml
  1483. [source,xml,role="secondary"]
  1484. ----
  1485. <http authorization-manager-ref="requestAuthorization"/>
  1486. ----
  1487. ====
  1488. [NOTE]
  1489. ====
  1490. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1491. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1492. ====
  1493. ===== I use `ConsensusBased`
  1494. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  1495. 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.
  1496. 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`].
  1497. ===== I use a custom `AccessDecisionVoter`
  1498. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  1499. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  1500. 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:
  1501. ====
  1502. .Java
  1503. [source,java,role="primary"]
  1504. ----
  1505. public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
  1506. private final SecurityMetadataSource metadata;
  1507. private final AccessDecisionVoter voter;
  1508. public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
  1509. Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
  1510. AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
  1511. this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
  1512. WebExpressionVoter voter = new WebExpressionVoter();
  1513. voter.setExpressionHandler(expressionHandler);
  1514. this.voter = voter;
  1515. }
  1516. public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
  1517. List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
  1518. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  1519. if (decision == ACCESS_GRANTED) {
  1520. return new AuthorizationDecision(true);
  1521. }
  1522. if (decision == ACCESS_DENIED) {
  1523. return new AuthorizationDecision(false);
  1524. }
  1525. return null; // abstain
  1526. }
  1527. }
  1528. ----
  1529. ====
  1530. 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`].
  1531. [[servlet-authorizationmanager-requests-opt-out]]
  1532. ==== Opt-out Steps
  1533. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  1534. ===== I cannot secure all dispatcher types
  1535. If you cannot secure all dispatcher types, first try and declare which dispatcher types should not require authorization like so:
  1536. ====
  1537. .Java
  1538. [source,java,role="primary"]
  1539. ----
  1540. http
  1541. .authorizeHttpRequests((authorize) -> authorize
  1542. .shouldFilterAllDispatcherTypes(true)
  1543. .dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
  1544. .mvcMatchers("/app/**").hasRole("APP")
  1545. // ...
  1546. .anyRequest().denyAll()
  1547. )
  1548. // ...
  1549. ----
  1550. .Kotlin
  1551. [source,kotlin,role="secondary"]
  1552. ----
  1553. http {
  1554. authorizeHttpRequests {
  1555. shouldFilterAllDispatcherTypes = true
  1556. authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
  1557. authorize("/app/**", hasRole("APP"))
  1558. // ...
  1559. authorize(anyRequest, denyAll)
  1560. }
  1561. }
  1562. ----
  1563. .Xml
  1564. [source,xml,role="secondary"]
  1565. ----
  1566. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1567. <intercept-url request-matcher-ref="dispatchers"/>
  1568. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1569. <!-- ... -->
  1570. <intercept-url pattern="/**" access="denyAll"/>
  1571. </http>
  1572. <bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  1573. <constructor-arg>
  1574. <util:list value-type="javax.servlet.DispatcherType">
  1575. <value>FORWARD</value>
  1576. <value>INCLUDE</value>
  1577. </util:list>
  1578. </constructor-arg>
  1579. </bean>
  1580. ----
  1581. ====
  1582. 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`:
  1583. ====
  1584. .Java
  1585. [source,java,role="primary"]
  1586. ----
  1587. http
  1588. .authorizeHttpRequests((authorize) -> authorize
  1589. .filterAllDispatcherTypes(false)
  1590. .mvcMatchers("/app/**").hasRole("APP")
  1591. // ...
  1592. )
  1593. // ...
  1594. ----
  1595. .Kotlin
  1596. [source,kotlin,role="secondary"]
  1597. ----
  1598. http {
  1599. authorizeHttpRequests {
  1600. filterAllDispatcherTypes = false
  1601. authorize("/messages/**", hasRole("APP"))
  1602. // ...
  1603. }
  1604. }
  1605. ----
  1606. .Xml
  1607. [source,xml,role="secondary"]
  1608. ----
  1609. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1610. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1611. <!-- ... -->
  1612. </http>
  1613. ----
  1614. ====
  1615. or, if you are still using `authorizeRequests` or `use-authorization-manager="false"`, set `oncePerRequest` to `true`:
  1616. ====
  1617. .Java
  1618. [source,java,role="primary"]
  1619. ----
  1620. http
  1621. .authorizeRequests((authorize) -> authorize
  1622. .filterSecurityInterceptorOncePerRequest(true)
  1623. .mvcMatchers("/app/**").hasRole("APP")
  1624. // ...
  1625. )
  1626. // ...
  1627. ----
  1628. .Kotlin
  1629. [source,kotlin,role="secondary"]
  1630. ----
  1631. http {
  1632. authorizeRequests {
  1633. filterSecurityInterceptorOncePerRequest = true
  1634. authorize("/messages/**", hasRole("APP"))
  1635. // ...
  1636. }
  1637. }
  1638. ----
  1639. .Xml
  1640. [source,xml,role="secondary"]
  1641. ----
  1642. <http once-per-request="true" use-authorization-manager="false">
  1643. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1644. <!-- ... -->
  1645. </http>
  1646. ----
  1647. ====
  1648. ===== I cannot declare an authorization rule for all requests
  1649. 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:
  1650. ====
  1651. .Java
  1652. [source,java,role="primary"]
  1653. ----
  1654. http
  1655. .authorizeHttpReqeusts((authorize) -> authorize
  1656. .mvcMatchers("/app/*").hasRole("APP")
  1657. // ...
  1658. .anyRequest().permitAll()
  1659. )
  1660. ----
  1661. .Kotlin
  1662. [source,kotlin,role="secondary"]
  1663. ----
  1664. http {
  1665. authorizeHttpRequests {
  1666. authorize("/app*", hasRole("APP"))
  1667. // ...
  1668. authorize(anyRequest, permitAll)
  1669. }
  1670. }
  1671. ----
  1672. .Xml
  1673. [source,xml,role="secondary"]
  1674. ----
  1675. <http>
  1676. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1677. <!-- ... -->
  1678. <intercept-url pattern="/**" access="permitAll"/>
  1679. </http>
  1680. ----
  1681. ====
  1682. ===== I cannot migrate my SpEL or my `AccessDecisionManager`
  1683. 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.
  1684. 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.
  1685. Second, if you still need your custom `access-decision-manager-ref` or have some other reason to opt out of `AuthorizationManager`, do:
  1686. ====
  1687. .Xml
  1688. [source,xml,role="secondary"]
  1689. ----
  1690. <http use-authorization-manager="false">
  1691. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1692. <!-- ... -->
  1693. </http>
  1694. ----
  1695. ====
  1696. === Propagate ``AuthenticationServiceException``s
  1697. {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`].
  1698. 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.
  1699. ==== Configure `AuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  1700. To prepare for the 6.0 default, wire `AuthenticationFilter` instances with a `AuthenticationFailureHandler` that rethrows ``AuthenticationServiceException``s, like so:
  1701. ====
  1702. .Java
  1703. [source,java,role="primary"]
  1704. ----
  1705. AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
  1706. AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
  1707. handler.setRethrowAuthenticationServiceException(true);
  1708. authenticationFilter.setAuthenticationFailureHandler(handler);
  1709. ----
  1710. .Kotlin
  1711. [source,kotlin,role="secondary"]
  1712. ----
  1713. val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
  1714. val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
  1715. handler.setRethrowAuthenticationServiceException(true)
  1716. authenticationFilter.setAuthenticationFailureHandler(handler)
  1717. ----
  1718. .Xml
  1719. [source,xml,role="secondary"]
  1720. ----
  1721. <bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
  1722. <!-- ... -->
  1723. <property ref="authenticationFailureHandler"/>
  1724. </bean>
  1725. <bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
  1726. <property name="rethrowAuthenticationServiceException" value="true"/>
  1727. </bean>
  1728. ----
  1729. ====
  1730. [[servlet-authenticationfailurehandler-opt-out]]
  1731. ==== Opt-out Steps
  1732. If rethrowing ``AuthenticationServiceException``s gives you trouble, you can set the value to false instead of taking the 6.0 default, like so:
  1733. ====
  1734. .Java
  1735. [source,java,role="primary"]
  1736. ----
  1737. AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
  1738. AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
  1739. handler.setRethrowAuthenticationServiceException(false);
  1740. authenticationFilter.setAuthenticationFailureHandler(handler);
  1741. ----
  1742. .Kotlin
  1743. [source,kotlin,role="secondary"]
  1744. ----
  1745. val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
  1746. val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
  1747. handler.setRethrowAuthenticationServiceException(false)
  1748. authenticationFilter.setAuthenticationFailureHandler(handler)
  1749. ----
  1750. .Xml
  1751. [source,xml,role="secondary"]
  1752. ----
  1753. <bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
  1754. <!-- ... -->
  1755. <property ref="authenticationFailureHandler"/>
  1756. </bean>
  1757. <bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
  1758. <property name="rethrowAuthenticationServiceException" value="false"/>
  1759. </bean>
  1760. ----
  1761. ====
  1762. [[servlet-opt-in-sha256-rememberme]]
  1763. === Use SHA-256 in Remember Me
  1764. The `TokenBasedRememberMeServices` implementation now supports SHA-256 for the Remember Me token and this is the default in Spring Security 6.
  1765. 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.
  1766. 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.
  1767. If the algorithm name is not present, then the `matchingAlgorithm` property is used to check the token.
  1768. This allows for a smooth transition from MD5 to SHA-256.
  1769. 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.
  1770. 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.
  1771. [[servlet-opt-in-sha256-sha256-encoding]]
  1772. .Use Spring Security 6 defaults for encoding, SHA-256 for encoding and MD5 for matching
  1773. ====
  1774. .Java
  1775. [source,java,role="primary"]
  1776. ----
  1777. @Configuration
  1778. @EnableWebSecurity
  1779. public class SecurityConfig {
  1780. @Bean
  1781. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1782. http
  1783. // ...
  1784. .rememberMe((remember) -> remember
  1785. .rememberMeServices(rememberMeServices)
  1786. );
  1787. return http.build();
  1788. }
  1789. @Bean
  1790. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1791. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
  1792. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1793. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
  1794. return rememberMe;
  1795. }
  1796. }
  1797. ----
  1798. .XML
  1799. [source,xml,role="secondary"]
  1800. ----
  1801. <http>
  1802. <remember-me services-ref="rememberMeServices"/>
  1803. </http>
  1804. <bean id="rememberMeServices" class=
  1805. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1806. <property name="userDetailsService" ref="myUserDetailsService"/>
  1807. <property name="key" value="springRocks"/>
  1808. <property name="matchingAlgorithm" value="MD5"/>
  1809. <property name="encodingAlgorithm" value="SHA256"/>
  1810. </bean>
  1811. ----
  1812. ====
  1813. 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?
  1814. 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).
  1815. By that time, all the tokens generated with MD5 will have expired.
  1816. .Use Spring Security 6 defaults, SHA-256 for both encoding and matching
  1817. ====
  1818. .Java
  1819. [source,java,role="primary"]
  1820. ----
  1821. @Configuration
  1822. @EnableWebSecurity
  1823. public class SecurityConfig {
  1824. @Bean
  1825. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1826. http
  1827. // ...
  1828. .rememberMe((remember) -> remember
  1829. .rememberMeServices(rememberMeServices)
  1830. );
  1831. return http.build();
  1832. }
  1833. @Bean
  1834. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1835. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
  1836. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1837. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
  1838. return rememberMe;
  1839. }
  1840. }
  1841. ----
  1842. .XML
  1843. [source,xml,role="secondary"]
  1844. ----
  1845. <http>
  1846. <remember-me services-ref="rememberMeServices"/>
  1847. </http>
  1848. <bean id="rememberMeServices" class=
  1849. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1850. <property name="userDetailsService" ref="myUserDetailsService"/>
  1851. <property name="key" value="springRocks"/>
  1852. <property name="matchingAlgorithm" value="SHA256"/>
  1853. <property name="encodingAlgorithm" value="SHA256"/>
  1854. </bean>
  1855. ----
  1856. ====
  1857. If you are having problems with the Spring Security 6 defaults, you can explicitly opt into 5.8 defaults using the following configuration:
  1858. .Use MD5 for both encoding and matching algorithms
  1859. ====
  1860. .Java
  1861. [source,java,role="primary"]
  1862. ----
  1863. @Configuration
  1864. @EnableWebSecurity
  1865. public class SecurityConfig {
  1866. @Bean
  1867. SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
  1868. http
  1869. // ...
  1870. .rememberMe((remember) -> remember
  1871. .rememberMeServices(rememberMeServices)
  1872. );
  1873. return http.build();
  1874. }
  1875. @Bean
  1876. RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
  1877. RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.MD5;
  1878. TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
  1879. rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
  1880. return rememberMe;
  1881. }
  1882. }
  1883. ----
  1884. .XML
  1885. [source,xml,role="secondary"]
  1886. ----
  1887. <http>
  1888. <remember-me services-ref="rememberMeServices"/>
  1889. </http>
  1890. <bean id="rememberMeServices" class=
  1891. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  1892. <property name="userDetailsService" ref="myUserDetailsService"/>
  1893. <property name="key" value="springRocks"/>
  1894. <property name="matchingAlgorithm" value="MD5"/>
  1895. <property name="encodingAlgorithm" value="MD5"/>
  1896. </bean>
  1897. ----
  1898. ====
  1899. === Stop Using SAML 2.0 `Converter` constructors
  1900. In an early release of Spring Security's SAML 2.0 support, `Saml2MetadataFilter` and `Saml2AuthenticationTokenConverter` shipped with constructors of type `Converter`.
  1901. This level of abstraction made it tricky to evolve the class and so a dedicated interface `RelyingPartyRegistrationResolver` was introduced in a later release.
  1902. In 6.0, the `Converter` constructors are removed.
  1903. To prepare for this in 5.8, change classes that implement `Converter<HttpServletRequest, RelyingPartyRegistration>` to instead implement `RelyingPartyRegistrationResolver`.
  1904. === Change to Using `Saml2AuthenticationRequestResolver`
  1905. `Saml2AuthenticationContextResolver` and `Saml2AuthenticationRequestFactory` are removed in 6.0 as is the `Saml2WebSsoAuthenticationRequestFilter` that requires them.
  1906. They are replaced by `Saml2AuthenticationRequestResolver` and a new constructor in `Saml2WebSsoAuthenticationRequestFilter`.
  1907. The new interface removes an unnecessary transport object between the two classes.
  1908. Most applications need do nothing; however, if you use or configure `Saml2AuthenticationRequestContextResolver` or `Saml2AuthenticationRequestFactory`, try the following steps to convert instead use `Saml2AuthenticationRequestResolver`.
  1909. ==== Use `setAuthnRequestCustomizer` instead of `setAuthenticationRequestContextConverter`
  1910. If you are calling `OpenSaml4AuthenticationReqeustFactory#setAuthenticationRequestContextConverter`, for example, like so:
  1911. ====
  1912. .Java
  1913. [source,java,role="primary"]
  1914. ----
  1915. @Bean
  1916. Saml2AuthenticationRequestFactory authenticationRequestFactory() {
  1917. OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
  1918. factory.setAuthenticationRequestContextConverter((context) -> {
  1919. AuthnRequestBuilder authnRequestBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
  1920. .getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
  1921. IssuerBuilder issuerBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
  1922. .getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
  1923. tring issuer = context.getIssuer();
  1924. String destination = context.getDestination();
  1925. String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl();
  1926. String protocolBinding = context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
  1927. AuthnRequest auth = authnRequestBuilder.buildObject();
  1928. auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
  1929. auth.setIssueInstant(Instant.now());
  1930. auth.setForceAuthn(Boolean.TRUE);
  1931. auth.setIsPassive(Boolean.FALSE);
  1932. auth.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
  1933. Issuer iss = issuerBuilder.buildObject();
  1934. iss.setValue(issuer);
  1935. auth.setIssuer(iss);
  1936. auth.setDestination(destination);
  1937. auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl);
  1938. });
  1939. return factory;
  1940. }
  1941. ----
  1942. ====
  1943. to ensure that ForceAuthn is set to `true`, you can instead do:
  1944. ====
  1945. .Java
  1946. [source,java,role="primary"]
  1947. ----
  1948. @Bean
  1949. Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationResolver registrations) {
  1950. OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
  1951. resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest().setForceAuthn(Boolean.TRUE));
  1952. return resolver;
  1953. }
  1954. ----
  1955. ====
  1956. Also, since `setAuthnRequestCustomizer` has direct access to the `HttpServletRequest`, there is no need for a `Saml2AuthenticationRequestContextResolver`.
  1957. Simply use `setAuthnRequestCustomizer` to read directly from `HttpServletRequest` this information you need.
  1958. ==== Use `setAuthnRequestCustomizer` instead of `setProtocolBinding`
  1959. Instead of doing:
  1960. ====
  1961. .Java
  1962. [source,java,role="primary"]
  1963. ----
  1964. @Bean
  1965. Saml2AuthenticationRequestFactory authenticationRequestFactory() {
  1966. OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
  1967. factory.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
  1968. return factory;
  1969. }
  1970. ----
  1971. ====
  1972. you can do:
  1973. ====
  1974. .Java
  1975. [source,java,role="primary"]
  1976. ----
  1977. @Bean
  1978. Saml2AuthenticationRequestResolver authenticationRequestResolver() {
  1979. OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
  1980. resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest()
  1981. .setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"));
  1982. return resolver;
  1983. }
  1984. ----
  1985. ====
  1986. [NOTE]
  1987. ====
  1988. 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.
  1989. ====
  1990. === Use the latest `Saml2AuthenticationToken` constructor
  1991. In an early release, `Saml2AuthenticationToken` took several individual settings as constructor parameters.
  1992. This created a challenge each time a new parameter needed to be added.
  1993. 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.
  1994. It also is valuable in that it more closely aligns with the design of `OAuth2LoginAuthenticationToken`.
  1995. Most applications do not construct this class directly since `Saml2WebSsoAuthenticationFilter` does.
  1996. However, in the event that your application constructs one, please change from:
  1997. ====
  1998. .Java
  1999. [source,java,role="primary"]
  2000. ----
  2001. new Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
  2002. registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
  2003. ----
  2004. .Kotlin
  2005. [source,kotlin,role="secondary"]
  2006. ----
  2007. Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
  2008. registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
  2009. ----
  2010. ====
  2011. to:
  2012. ====
  2013. .Java
  2014. [source,java,role="primary"]
  2015. ----
  2016. new Saml2AuthenticationToken(saml2Response, registration)
  2017. ----
  2018. .Kotlin
  2019. [source,kotlin,role="secondary"]
  2020. ----
  2021. Saml2AuthenticationToken(saml2Response, registration)
  2022. ----
  2023. ====
  2024. === Use `RelyingPartyRegistration` updated methods
  2025. In an early release of Spring Security's SAML support, there was some ambiguity on the meaning of certain `RelyingPartyRegistration` methods and their function.
  2026. 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.
  2027. The deprecated methods in `RelyingPartyRegstration` are removed.
  2028. To prepare for that, consider the following representative usage of `RelyingPartyRegistration`:
  2029. ====
  2030. .Java
  2031. [source,java,role="primary"]
  2032. ----
  2033. String idpEntityId = registration.getRemoteIdpEntityId();
  2034. String assertionConsumerServiceUrl = registration.getAssertionConsumerServiceUrlTemplate();
  2035. String idpWebSsoUrl = registration.getIdpWebSsoUrl();
  2036. String localEntityId = registration.getLocalEntityIdTemplate();
  2037. List<Saml2X509Credential> verifying = registration.getCredentials().stream()
  2038. .filter(Saml2X509Credential::isSignatureVerficationCredential)
  2039. .collect(Collectors.toList());
  2040. ----
  2041. .Kotlin
  2042. [source,kotlin,role="secondary"]
  2043. ----
  2044. val idpEntityId: String = registration.getRemoteIdpEntityId()
  2045. val assertionConsumerServiceUrl: String = registration.getAssertionConsumerServiceUrlTemplate()
  2046. val idpWebSsoUrl: String = registration.getIdpWebSsoUrl()
  2047. val localEntityId: String = registration.getLocalEntityIdTemplate()
  2048. val verifying: List<Saml2X509Credential> = registration.getCredentials()
  2049. .filter(Saml2X509Credential::isSignatureVerficationCredential)
  2050. ----
  2051. ====
  2052. This should change to:
  2053. ====
  2054. .Java
  2055. [source,java,role="primary"]
  2056. ----
  2057. String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId();
  2058. String assertionConsumerServiceLocation = registration.getAssertionConsumerServiceLocation();
  2059. String singleSignOnServiceLocation = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation();
  2060. String entityId = registration.getEntityId();
  2061. List<Saml2X509Credential> verifying = registration.getAssertingPartyDetails().getVerificationX509Credentials();
  2062. ----
  2063. .Kotlin
  2064. [source,kotlin,role="secondary"]
  2065. ----
  2066. val assertingPartyEntityId: String = registration.getAssertingPartyDetails().getEntityId()
  2067. val assertionConsumerServiceLocation: String = registration.getAssertionConsumerServiceLocation()
  2068. val singleSignOnServiceLocation: String = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation()
  2069. val entityId: String = registration.getEntityId()
  2070. val verifying: List<Saml2X509Credential> = registration.getAssertingPartyDetails().getVerificationX509Credentials()
  2071. ----
  2072. ====
  2073. 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].
  2074. === Use OpenSAML 4
  2075. OpenSAML 3 has reached its end-of-life.
  2076. As such, Spring Security 6 drops support for it, bumping up its OpenSAML baseline to 4.
  2077. To prepare for the upgrade, update your pom to depend on OpenSAML 4 instead of 3:
  2078. ====
  2079. .Maven
  2080. [source,maven,role="primary"]
  2081. ----
  2082. <dependencyManagement>
  2083. <dependency>
  2084. <groupId>org.opensaml</groupId>
  2085. <artifactId>opensaml-core</artifactId>
  2086. <version>4.2.1</version>
  2087. </dependency>
  2088. <dependency>
  2089. <groupId>org.opensaml</groupId>
  2090. <artifactId>opensaml-saml-api</artifactId>
  2091. <version>4.2.1</version>
  2092. </dependency>
  2093. <dependency>
  2094. <groupId>org.opensaml</groupId>
  2095. <artifactId>opensaml-saml-impl</artifactId>
  2096. <version>4.2.1</version>
  2097. </dependency>
  2098. </dependencyManagement>
  2099. ----
  2100. .Gradle
  2101. [source,gradle,role="secondary"]
  2102. ----
  2103. dependencies {
  2104. constraints {
  2105. api "org.opensaml:opensaml-core:4.2.1"
  2106. api "org.opensaml:opensaml-saml-api:4.2.1"
  2107. api "org.opensaml:opensaml-saml-impl:4.2.1"
  2108. }
  2109. }
  2110. ----
  2111. ====
  2112. You must use at least OpenSAML 4.1.1 to update to Spring Security 6's SAML support.
  2113. === Use `OpenSaml4AuthenticationProvider`
  2114. In order to support both OpenSAML 3 and 4 at the same time, Spring Security released `OpenSamlAuthenticationProvider` and `OpenSaml4AuthenticationProvider`.
  2115. In 6.0, because OpenSAML3 support is removed, `OpenSamlAuthenticationProvider` is removed as well.
  2116. Not all methods in `OpenSamlAuthenticationProvider` were ported 1-to-1 to `OpenSaml4AuthenticationProvider`.
  2117. As such, some adjustment will be required to make the challenge.
  2118. Consider the following representative usage of `OpenSamlAuthenticationProvider`:
  2119. ====
  2120. .Java
  2121. [source,java,role="primary"]
  2122. ----
  2123. OpenSamlAuthenticationProvider versionThree = new OpenSamlAuthenticationProvider();
  2124. versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor);
  2125. versionThree.setResponseTimeValidationSkew(myDuration);
  2126. ----
  2127. .Kotlin
  2128. [source,kotlin,role="secondary"]
  2129. ----
  2130. val versionThree: OpenSamlAuthenticationProvider = OpenSamlAuthenticationProvider()
  2131. versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor)
  2132. versionThree.setResponseTimeValidationSkew(myDuration)
  2133. ----
  2134. ====
  2135. This should change to:
  2136. ====
  2137. .Java
  2138. [source,java,role="primary"]
  2139. ----
  2140. Converter<ResponseToken, Saml2Authentication> delegate = OpenSaml4AuthenticationProvider
  2141. .createDefaultResponseAuthenticationConverter();
  2142. OpenSaml4AuthenticationProvider versionFour = new OpenSaml4AuthenticationProvider();
  2143. versionFour.setResponseAuthenticationConverter((responseToken) -> {
  2144. Saml2Authentication authentication = delegate.convert(responseToken);
  2145. Assertion assertion = responseToken.getResponse().getAssertions().get(0);
  2146. AuthenticatedPrincipal principal = (AuthenticatedPrincipal) authentication.getPrincipal();
  2147. Collection<GrantedAuthority> authorities = myAuthoritiesExtractor.convert(assertion);
  2148. return new Saml2Authentication(principal, authentication.getSaml2Response(), authorities);
  2149. });
  2150. Converter<AssertionToken, Saml2ResponseValidationResult> validator = OpenSaml4AuthenticationProvider
  2151. .createDefaultAssertionValidatorWithParameters((p) -> p.put(CLOCK_SKEW, myDuration));
  2152. versionFour.setAssertionValidator(validator);
  2153. ----
  2154. .Kotlin
  2155. [source,kotlin,role="secondary"]
  2156. ----
  2157. val delegate = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
  2158. val versionFour = OpenSaml4AuthenticationProvider()
  2159. versionFour.setResponseAuthenticationConverter({
  2160. responseToken -> {
  2161. val authentication = delegate.convert(responseToken)
  2162. val assertion = responseToken.getResponse().getAssertions().get(0)
  2163. val principal = (AuthenticatedPrincipal) authentication.getPrincipal()
  2164. val authorities = myAuthoritiesExtractor.convert(assertion)
  2165. return Saml2Authentication(principal, authentication.getSaml2Response(), authorities)
  2166. }
  2167. })
  2168. val validator = OpenSaml4AuthenticationProvider
  2169. .createDefaultAssertionValidatorWithParameters({ p -> p.put(CLOCK_SKEW, myDuration) })
  2170. versionFour.setAssertionValidator(validator)
  2171. ----
  2172. ====
  2173. [[use-new-requestmatchers]]
  2174. === Use the new `requestMatchers` methods
  2175. 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].
  2176. 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.
  2177. The deprecated methods are removed in Spring Security 6.
  2178. These new methods have more secure defaults since they choose the most appropriate `RequestMatcher` implementation for your application.
  2179. 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).
  2180. To start using the new methods, you can replace the deprecated methods with the new ones. For example, the following application configuration:
  2181. ====
  2182. .Java
  2183. [source,java,role="primary"]
  2184. ----
  2185. @Configuration
  2186. @EnableWebSecurity
  2187. public class SecurityConfig {
  2188. @Bean
  2189. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2190. http
  2191. .authorizeHttpRequests((authz) -> authz
  2192. .antMatchers("/api/admin/**").hasRole("ADMIN")
  2193. .antMatchers("/api/user/**").hasRole("USER")
  2194. .anyRequest().authenticated()
  2195. );
  2196. return http.build();
  2197. }
  2198. }
  2199. ----
  2200. ====
  2201. can be changed to:
  2202. ====
  2203. .Java
  2204. [source,java,role="primary"]
  2205. ----
  2206. @Configuration
  2207. @EnableWebSecurity
  2208. public class SecurityConfig {
  2209. @Bean
  2210. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2211. http
  2212. .authorizeHttpRequests((authz) -> authz
  2213. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2214. .requestMatchers("/api/user/**").hasRole("USER")
  2215. .anyRequest().authenticated()
  2216. );
  2217. return http.build();
  2218. }
  2219. }
  2220. ----
  2221. ====
  2222. 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.
  2223. The following configuration:
  2224. ====
  2225. .Java
  2226. [source,java,role="primary"]
  2227. ----
  2228. @Configuration
  2229. @EnableWebSecurity
  2230. @EnableWebMvc
  2231. public class SecurityConfig {
  2232. @Bean
  2233. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2234. http
  2235. .authorizeHttpRequests((authz) -> authz
  2236. .mvcMatchers("/admin/**").hasRole("ADMIN")
  2237. .anyRequest().authenticated()
  2238. );
  2239. return http.build();
  2240. }
  2241. }
  2242. ----
  2243. ====
  2244. is equivalent to:
  2245. ====
  2246. .Java
  2247. [source,java,role="primary"]
  2248. ----
  2249. @Configuration
  2250. @EnableWebSecurity
  2251. @EnableWebMvc
  2252. public class SecurityConfig {
  2253. @Bean
  2254. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2255. http
  2256. .authorizeHttpRequests((authz) -> authz
  2257. .requestMatchers("/admin/**").hasRole("ADMIN")
  2258. .anyRequest().authenticated()
  2259. );
  2260. return http.build();
  2261. }
  2262. }
  2263. ----
  2264. ====
  2265. 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:
  2266. ====
  2267. .Java
  2268. [source,java,role="primary"]
  2269. ----
  2270. @Configuration
  2271. @EnableWebSecurity
  2272. @EnableWebMvc
  2273. public class SecurityConfig {
  2274. @Bean
  2275. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2276. http
  2277. .authorizeHttpRequests((authz) -> authz
  2278. .mvcMatchers("/admin").servletPath("/path").hasRole("ADMIN")
  2279. .mvcMatchers("/user").servletPath("/path").hasRole("USER")
  2280. .anyRequest().authenticated()
  2281. );
  2282. return http.build();
  2283. }
  2284. }
  2285. ----
  2286. ====
  2287. The code above can be rewritten using the `MvcRequestMatcher.Builder` and the `requestMatchers` method:
  2288. ====
  2289. .Java
  2290. [source,java,role="primary"]
  2291. ----
  2292. @Configuration
  2293. @EnableWebSecurity
  2294. @EnableWebMvc
  2295. public class SecurityConfig {
  2296. @Bean
  2297. SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
  2298. MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
  2299. http
  2300. .authorizeHttpRequests((authz) -> authz
  2301. .requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
  2302. .requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
  2303. .anyRequest().authenticated()
  2304. );
  2305. return http.build();
  2306. }
  2307. }
  2308. ----
  2309. ====
  2310. If you are having problem with the new `requestMatchers` methods, you can always switch back to the `RequestMatcher` implementation that you were using.
  2311. For example, if you still want to use `AntPathRequestMatcher` and `RegexRequestMatcher` implementations, you can use the `requestMatchers` method that accepts a `RequestMatcher` instance:
  2312. ====
  2313. .Java
  2314. [source,java,role="primary"]
  2315. ----
  2316. import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
  2317. import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;
  2318. @Configuration
  2319. @EnableWebSecurity
  2320. public class SecurityConfig {
  2321. @Bean
  2322. SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2323. http
  2324. .authorizeHttpRequests((authz) -> authz
  2325. .requestMatchers(antMatcher("/user/**")).hasRole("USER")
  2326. .requestMatchers(antMatcher(HttpMethod.POST, "/user/**")).hasRole("ADMIN")
  2327. .requestMatchers(regexMatcher(".*\\?x=y")).hasRole("SPECIAL") // matches /any/path?x=y
  2328. .anyRequest().authenticated()
  2329. );
  2330. return http.build();
  2331. }
  2332. }
  2333. ----
  2334. ====
  2335. 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.
  2336. If you are using the `WebSecurityCustomizer` interface, you can replace the deprecated `antMatchers` methods:
  2337. ====
  2338. .Java
  2339. [source,java,role="primary"]
  2340. ----
  2341. @Bean
  2342. public WebSecurityCustomizer webSecurityCustomizer() {
  2343. return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
  2344. }
  2345. ----
  2346. ====
  2347. with their `requestMatchers` counterparts:
  2348. ====
  2349. .Java
  2350. [source,java,role="primary"]
  2351. ----
  2352. @Bean
  2353. public WebSecurityCustomizer webSecurityCustomizer() {
  2354. return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2");
  2355. }
  2356. ----
  2357. ====
  2358. The same way, if you are customizing the CSRF configuration to ignore some paths, you can replace the deprecated methods with the `requestMatchers` methods:
  2359. ====
  2360. .Java
  2361. [source,java,role="primary"]
  2362. ----
  2363. @Bean
  2364. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2365. http
  2366. .csrf((csrf) -> csrf
  2367. .ignoringAntMatchers("/no-csrf")
  2368. );
  2369. return http.build();
  2370. }
  2371. ----
  2372. ====
  2373. can be changed to:
  2374. ====
  2375. .Java
  2376. [source,java,role="primary"]
  2377. ----
  2378. @Bean
  2379. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2380. http
  2381. .csrf((csrf) -> csrf
  2382. .ignoringRequestMatchers("/no-csrf")
  2383. );
  2384. return http.build();
  2385. }
  2386. ----
  2387. ====
  2388. [[use-new-security-matchers]]
  2389. === Use the new `securityMatchers` methods
  2390. In Spring Security 5.8, the `antMatchers`, `mvcMatchers` and `requestMatchers` methods from `HttpSecurity` were deprecated in favor of new `securityMatchers` methods.
  2391. Note that these methods are not the same from `authorizeHttpRequests` methods <<use-new-requestmatchers,which were deprecated>> in favor of the `requestMatchers` methods.
  2392. 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.
  2393. 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).
  2394. Another reason for adding the `securityMatchers` methods is to avoid confusion with the `requestMatchers` methods from `authorizeHttpRequests`.
  2395. The following configuration:
  2396. ====
  2397. .Java
  2398. [source,java,role="primary"]
  2399. ----
  2400. @Bean
  2401. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2402. http
  2403. .antMatcher("/api/**", "/app/**")
  2404. .authorizeHttpRequests((authz) -> authz
  2405. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2406. .anyRequest().authenticated()
  2407. );
  2408. return http.build();
  2409. }
  2410. ----
  2411. ====
  2412. can be rewritten using the `securityMatchers` methods:
  2413. ====
  2414. .Java
  2415. [source,java,role="primary"]
  2416. ----
  2417. @Bean
  2418. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2419. http
  2420. .securityMatcher("/api/**", "/app/**")
  2421. .authorizeHttpRequests((authz) -> authz
  2422. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2423. .anyRequest().authenticated()
  2424. );
  2425. return http.build();
  2426. }
  2427. ----
  2428. ====
  2429. If you are using a custom `RequestMatcher` in your `HttpSecurity` configuration:
  2430. ====
  2431. .Java
  2432. [source,java,role="primary"]
  2433. ----
  2434. @Bean
  2435. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2436. http
  2437. .requestMatcher(new MyCustomRequestMatcher())
  2438. .authorizeHttpRequests((authz) -> authz
  2439. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2440. .anyRequest().authenticated()
  2441. );
  2442. return http.build();
  2443. }
  2444. public class MyCustomRequestMatcher implements RequestMatcher {
  2445. // ...
  2446. }
  2447. ----
  2448. ====
  2449. you can do the same using `securityMatcher`:
  2450. ====
  2451. .Java
  2452. [source,java,role="primary"]
  2453. ----
  2454. @Bean
  2455. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2456. http
  2457. .securityMatcher(new MyCustomRequestMatcher())
  2458. .authorizeHttpRequests((authz) -> authz
  2459. .requestMatchers("/api/admin/**").hasRole("ADMIN")
  2460. .anyRequest().authenticated()
  2461. );
  2462. return http.build();
  2463. }
  2464. public class MyCustomRequestMatcher implements RequestMatcher {
  2465. // ...
  2466. }
  2467. ----
  2468. ====
  2469. If you are combining multiple `RequestMatcher` implementations in your `HttpSecurity` configuration:
  2470. ====
  2471. .Java
  2472. [source,java,role="primary"]
  2473. ----
  2474. @Bean
  2475. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2476. http
  2477. .requestMatchers((matchers) -> matchers
  2478. .antMatchers("/api/**", "/app/**")
  2479. .mvcMatchers("/admin/**")
  2480. .requestMatchers(new MyCustomRequestMatcher())
  2481. )
  2482. .authorizeHttpRequests((authz) -> authz
  2483. .requestMatchers("/admin/**").hasRole("ADMIN")
  2484. .anyRequest().authenticated()
  2485. );
  2486. return http.build();
  2487. }
  2488. ----
  2489. ====
  2490. you can change it by using `securityMatchers`:
  2491. ====
  2492. .Java
  2493. [source,java,role="primary"]
  2494. ----
  2495. @Bean
  2496. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2497. http
  2498. .securityMatchers((matchers) -> matchers
  2499. .requestMatchers("/api/**", "/app/**", "/admin/**")
  2500. .requestMatchers(new MyCustomRequestMatcher())
  2501. )
  2502. .authorizeHttpRequests((authz) -> authz
  2503. .requestMatchers("/admin/**").hasRole("ADMIN")
  2504. .anyRequest().authenticated()
  2505. );
  2506. return http.build();
  2507. }
  2508. ----
  2509. ====
  2510. If you are having problems with the `securityMatchers` methods choosing the `RequestMatcher` implementation for you, you can always choose the `RequestMatcher` implementation yourself:
  2511. ====
  2512. .Java
  2513. [source,java,role="primary"]
  2514. ----
  2515. import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
  2516. @Bean
  2517. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2518. http
  2519. .securityMatcher(antMatcher("/api/**"), antMatcher("/app/**"))
  2520. .authorizeHttpRequests((authz) -> authz
  2521. .requestMatchers(antMatcher("/api/admin/**")).hasRole("ADMIN")
  2522. .anyRequest().authenticated()
  2523. );
  2524. return http.build();
  2525. }
  2526. ----
  2527. ====
  2528. === Stop using `Encryptors.queryableText`
  2529. `Encryptors.queryableText(CharSequence,CharSequence)` is unsafe since https://tanzu.vmware.com/security/cve-2020-5408[the same input data will produce the same output].
  2530. It was deprecated and will be removed in 6.0; Spring Security no longer supports encrypting data in this way.
  2531. To upgrade, you will either need to re-encrypt with a supported mechanism or store it decrypted.
  2532. Consider the following pseudocode for reading each encrypted entry from a table, decrypting it, and then re-encrypting it using a supported mechanism:
  2533. ====
  2534. .Java
  2535. [source,java,role="primary"]
  2536. ----
  2537. TextEncryptor deprecated = Encryptors.queryableText(password, salt);
  2538. BytesEncryptor aes = new AesBytesEncryptor(password, salt, KeyGenerators.secureRandom(12), CipherAlgorithm.GCM);
  2539. TextEncryptor supported = new HexEncodingTextEncryptor(aes);
  2540. for (MyEntry entry : entries) {
  2541. String value = deprecated.decrypt(entry.getEncryptedValue()); <1>
  2542. entry.setEncryptedValue(supported.encrypt(value)); <2>
  2543. entryService.save(entry)
  2544. }
  2545. ----
  2546. ====
  2547. <1> - The above uses the deprecated `queryableText` to convert the value to plaintext.
  2548. <2> - Then, the value is re-encrypted with a supported Spring Security mechanism.
  2549. Please see the reference manual for more information on what xref:features/integrations/cryptography.adoc[encryption mechanisms Spring Security supports].
  2550. === Default authorities for oauth2Login()
  2551. In Spring Security 5, the default `GrantedAuthority` given to a user that authenticates with an OAuth2 or OpenID Connect 1.0 provider (via `oauth2Login()`) is `ROLE_USER`.
  2552. [NOTE]
  2553. ====
  2554. See xref:servlet/oauth2/login/advanced.adoc#oauth2login-advanced-map-authorities[Mapping User Authorities] for more information.
  2555. ====
  2556. In Spring Security 6, the default authority given to a user authenticating with an OAuth2 provider is `OAUTH2_USER`.
  2557. The default authority given to a user authenticating with an OpenID Connect 1.0 provider is `OIDC_USER`.
  2558. These defaults allow clearer distinction of users that have authenticated with an OAuth2 or OpenID Connect 1.0 provider.
  2559. If you are using authorization rules or expressions such as `hasRole("USER")` or `hasAuthority("ROLE_USER")` to authorize users with this specific authority, the new defaults in Spring Security 6 will impact your application.
  2560. To opt into the new Spring Security 6 defaults, the following configuration can be used.
  2561. .Configure oauth2Login() with 6.0 defaults
  2562. ====
  2563. .Java
  2564. [source,java,role="primary"]
  2565. ----
  2566. @Bean
  2567. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2568. http
  2569. // ...
  2570. .oauth2Login((oauth2Login) -> oauth2Login
  2571. .userInfoEndpoint((userInfo) -> userInfo
  2572. .userAuthoritiesMapper(grantedAuthoritiesMapper())
  2573. )
  2574. );
  2575. return http.build();
  2576. }
  2577. private GrantedAuthoritiesMapper grantedAuthoritiesMapper() {
  2578. return (authorities) -> {
  2579. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  2580. authorities.forEach((authority) -> {
  2581. GrantedAuthority mappedAuthority;
  2582. if (authority instanceof OidcUserAuthority) {
  2583. OidcUserAuthority userAuthority = (OidcUserAuthority) authority;
  2584. mappedAuthority = new OidcUserAuthority(
  2585. "OIDC_USER", userAuthority.getIdToken(), userAuthority.getUserInfo());
  2586. } else if (authority instanceof OAuth2UserAuthority) {
  2587. OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) authority;
  2588. mappedAuthority = new OAuth2UserAuthority(
  2589. "OAUTH2_USER", userAuthority.getAttributes());
  2590. } else {
  2591. mappedAuthority = authority;
  2592. }
  2593. mappedAuthorities.add(mappedAuthority);
  2594. });
  2595. return mappedAuthorities;
  2596. };
  2597. }
  2598. ----
  2599. .Kotlin
  2600. [source,kotlin,role="secondary"]
  2601. ----
  2602. @Bean
  2603. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  2604. http {
  2605. // ...
  2606. oauth2Login {
  2607. userInfoEndpoint {
  2608. userAuthoritiesMapper = grantedAuthoritiesMapper()
  2609. }
  2610. }
  2611. }
  2612. return http.build()
  2613. }
  2614. private fun grantedAuthoritiesMapper(): GrantedAuthoritiesMapper {
  2615. return GrantedAuthoritiesMapper { authorities ->
  2616. authorities.map { authority ->
  2617. when (authority) {
  2618. is OidcUserAuthority ->
  2619. OidcUserAuthority("OIDC_USER", authority.idToken, authority.userInfo)
  2620. is OAuth2UserAuthority ->
  2621. OAuth2UserAuthority("OAUTH2_USER", authority.attributes)
  2622. else -> authority
  2623. }
  2624. }
  2625. }
  2626. }
  2627. ----
  2628. .XML
  2629. [source,xml,role="secondary"]
  2630. ----
  2631. <http>
  2632. <oauth2-login user-authorities-mapper-ref="userAuthoritiesMapper" ... />
  2633. </http>
  2634. ----
  2635. ====
  2636. [[servlet-oauth2-login-authorities-opt-out]]
  2637. ==== Opt-out Steps
  2638. If configuring the new authorities gives you trouble, you can opt out and explicitly use the 5.8 authority of `ROLE_USER` with the following configuration.
  2639. .Configure oauth2Login() with 5.8 defaults
  2640. ====
  2641. .Java
  2642. [source,java,role="primary"]
  2643. ----
  2644. @Bean
  2645. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  2646. http
  2647. // ...
  2648. .oauth2Login((oauth2Login) -> oauth2Login
  2649. .userInfoEndpoint((userInfo) -> userInfo
  2650. .userAuthoritiesMapper(grantedAuthoritiesMapper())
  2651. )
  2652. );
  2653. return http.build();
  2654. }
  2655. private GrantedAuthoritiesMapper grantedAuthoritiesMapper() {
  2656. return (authorities) -> {
  2657. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  2658. authorities.forEach((authority) -> {
  2659. GrantedAuthority mappedAuthority;
  2660. if (authority instanceof OidcUserAuthority) {
  2661. OidcUserAuthority userAuthority = (OidcUserAuthority) authority;
  2662. mappedAuthority = new OidcUserAuthority(
  2663. "ROLE_USER", userAuthority.getIdToken(), userAuthority.getUserInfo());
  2664. } else if (authority instanceof OAuth2UserAuthority) {
  2665. OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) authority;
  2666. mappedAuthority = new OAuth2UserAuthority(
  2667. "ROLE_USER", userAuthority.getAttributes());
  2668. } else {
  2669. mappedAuthority = authority;
  2670. }
  2671. mappedAuthorities.add(mappedAuthority);
  2672. });
  2673. return mappedAuthorities;
  2674. };
  2675. }
  2676. ----
  2677. .Kotlin
  2678. [source,kotlin,role="secondary"]
  2679. ----
  2680. @Bean
  2681. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  2682. http {
  2683. // ...
  2684. oauth2Login {
  2685. userInfoEndpoint {
  2686. userAuthoritiesMapper = grantedAuthoritiesMapper()
  2687. }
  2688. }
  2689. }
  2690. return http.build()
  2691. }
  2692. private fun grantedAuthoritiesMapper(): GrantedAuthoritiesMapper {
  2693. return GrantedAuthoritiesMapper { authorities ->
  2694. authorities.map { authority ->
  2695. when (authority) {
  2696. is OidcUserAuthority ->
  2697. OidcUserAuthority("ROLE_USER", authority.idToken, authority.userInfo)
  2698. is OAuth2UserAuthority ->
  2699. OAuth2UserAuthority("ROLE_USER", authority.attributes)
  2700. else -> authority
  2701. }
  2702. }
  2703. }
  2704. }
  2705. ----
  2706. .XML
  2707. [source,xml,role="secondary"]
  2708. ----
  2709. <http>
  2710. <oauth2-login user-authorities-mapper-ref="userAuthoritiesMapper" ... />
  2711. </http>
  2712. ----
  2713. ====
  2714. === Stop Using `WebSecurityConfigurerAdapter`
  2715. ==== Publish a `SecurityFilterChain` Bean
  2716. Spring Security 5.4 introduced the capability to publish a `SecurityFilterChain` bean instead of extending `WebSecurityConfigurerAdapter`.
  2717. In 6.0, `WebSecurityConfigurerAdapter` is removed.
  2718. To prepare for this change, you can replace constructs like:
  2719. ====
  2720. .Java
  2721. [source,java,role="primary"]
  2722. ----
  2723. @Configuration
  2724. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  2725. @Override
  2726. protected void configure(HttpSecurity http) throws Exception {
  2727. http
  2728. .authorizeHttpRequests((authorize) -> authorize
  2729. .anyRequest().authenticated()
  2730. )
  2731. .httpBasic(withDefaults());
  2732. }
  2733. }
  2734. ----
  2735. .Kotlin
  2736. [source,kotlin,role="secondary"]
  2737. ----
  2738. @Configuration
  2739. open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
  2740. @Override
  2741. override fun configure(val http: HttpSecurity) {
  2742. http {
  2743. authorizeHttpRequests {
  2744. authorize(anyRequest, authenticated)
  2745. }
  2746. httpBasic {}
  2747. }
  2748. }
  2749. }
  2750. ----
  2751. ====
  2752. with:
  2753. ====
  2754. .Java
  2755. [source,java,role="primary"]
  2756. ----
  2757. @Configuration
  2758. public class SecurityConfiguration {
  2759. @Bean
  2760. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  2761. http
  2762. .authorizeHttpRequests((authorize) -> authorize
  2763. .anyRequest().authenticated()
  2764. )
  2765. .httpBasic(withDefaults());
  2766. return http.build();
  2767. }
  2768. }
  2769. ----
  2770. .Kotlin
  2771. [source,kotlin,role="secondary"]
  2772. ----
  2773. @Configuration
  2774. open class SecurityConfiguration {
  2775. @Bean
  2776. fun filterChain(http: HttpSecurity): SecurityFilterChain {
  2777. http {
  2778. authorizeHttpRequests {
  2779. authorize(anyRequest, authenticated)
  2780. }
  2781. httpBasic {}
  2782. }
  2783. return http.build()
  2784. }
  2785. }
  2786. ----
  2787. ====
  2788. ==== Publish an `AuthenticationManager` Bean
  2789. As part of `WebSecurityConfigurerAdapeter` removal, `configure(AuthenticationManagerBuilder)` is also removed.
  2790. Preparing for its removal will differ based on your reason for using it.
  2791. ===== LDAP Authentication
  2792. If you are using `auth.ldapAuthentication()` for xref:servlet/authentication/passwords/ldap.adoc[LDAP authentication support], you can replace:
  2793. ====
  2794. .Java
  2795. [source,java,role="primary"]
  2796. ----
  2797. @Configuration
  2798. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  2799. @Override
  2800. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  2801. auth
  2802. .ldapAuthentication()
  2803. .userDetailsContextMapper(new PersonContextMapper())
  2804. .userDnPatterns("uid={0},ou=people")
  2805. .contextSource()
  2806. .port(0);
  2807. }
  2808. }
  2809. ----
  2810. .Kotlin
  2811. [source,kotlin,role="secondary"]
  2812. ----
  2813. @Configuration
  2814. open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
  2815. override fun configure(auth: AuthenticationManagerBuilder) {
  2816. auth
  2817. .ldapAuthentication()
  2818. .userDetailsContextMapper(PersonContextMapper())
  2819. .userDnPatterns("uid={0},ou=people")
  2820. .contextSource()
  2821. .port(0)
  2822. }
  2823. }
  2824. ----
  2825. ====
  2826. with:
  2827. ====
  2828. .Java
  2829. [source,java,role="primary"]
  2830. ----
  2831. @Configuration
  2832. public class SecurityConfiguration {
  2833. @Bean
  2834. public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
  2835. EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
  2836. EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
  2837. contextSourceFactoryBean.setPort(0);
  2838. return contextSourceFactoryBean;
  2839. }
  2840. @Bean
  2841. AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource contextSource) {
  2842. LdapBindAuthenticationManagerFactory factory =
  2843. new LdapBindAuthenticationManagerFactory(contextSource);
  2844. factory.setUserDnPatterns("uid={0},ou=people");
  2845. factory.setUserDetailsContextMapper(new PersonContextMapper());
  2846. return factory.createAuthenticationManager();
  2847. }
  2848. }
  2849. ----
  2850. .Kotlin
  2851. [source,kotlin,role="secondary"]
  2852. ----
  2853. @Configuration
  2854. open class SecurityConfiguration {
  2855. @Bean
  2856. fun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {
  2857. val contextSourceFactoryBean: EmbeddedLdapServerContextSourceFactoryBean =
  2858. EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer()
  2859. contextSourceFactoryBean.setPort(0)
  2860. return contextSourceFactoryBean
  2861. }
  2862. @Bean
  2863. fun ldapAuthenticationManager(val contextSource: BaseLdapPathContextSource): AuthenticationManager {
  2864. val factory = LdapBindAuthenticationManagerFactory(contextSource)
  2865. factory.setUserDnPatterns("uid={0},ou=people")
  2866. factory.setUserDetailsContextMapper(PersonContextMapper())
  2867. return factory.createAuthenticationManager()
  2868. }
  2869. }
  2870. ----
  2871. ====
  2872. ===== JDBC Authentication
  2873. If you are using `auth.jdbcAuthentication()` for xref:servlet/authentication/passwords/jdbc.adoc[JDBC Authentication support], you can replace:
  2874. ====
  2875. .Java
  2876. [source,java,role="primary"]
  2877. ----
  2878. @Configuration
  2879. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  2880. @Bean
  2881. public DataSource dataSource() {
  2882. return new EmbeddedDatabaseBuilder()
  2883. .setType(EmbeddedDatabaseType.H2)
  2884. .build();
  2885. }
  2886. @Override
  2887. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  2888. UserDetails user = User.withDefaultPasswordEncoder()
  2889. .username("user")
  2890. .password("password")
  2891. .roles("USER")
  2892. .build();
  2893. auth.jdbcAuthentication()
  2894. .withDefaultSchema()
  2895. .dataSource(this.dataSource)
  2896. .withUser(user);
  2897. }
  2898. }
  2899. ----
  2900. .Kotlin
  2901. [source,kotlin,role="secondary"]
  2902. ----
  2903. @Configuration
  2904. open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
  2905. @Bean
  2906. fun dataSource(): DataSource {
  2907. return EmbeddedDatabaseBuilder()
  2908. .setType(EmbeddedDatabaseType.H2)
  2909. .build()
  2910. }
  2911. override fun configure(val auth: AuthenticationManagerBuilder) {
  2912. UserDetails user = User.withDefaultPasswordEncoder()
  2913. .username("user")
  2914. .password("password")
  2915. .roles("USER")
  2916. .build()
  2917. auth.jdbcAuthentication()
  2918. .withDefaultSchema()
  2919. .dataSource(this.dataSource)
  2920. .withUser(user)
  2921. }
  2922. }
  2923. ----
  2924. ====
  2925. with:
  2926. ====
  2927. .Java
  2928. [source,java,role="primary"]
  2929. ----
  2930. @Configuration
  2931. public class SecurityConfiguration {
  2932. @Bean
  2933. public DataSource dataSource() {
  2934. return new EmbeddedDatabaseBuilder()
  2935. .setType(EmbeddedDatabaseType.H2)
  2936. .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
  2937. .build();
  2938. }
  2939. @Bean
  2940. public UserDetailsManager users(DataSource dataSource) {
  2941. UserDetails user = User.withDefaultPasswordEncoder()
  2942. .username("user")
  2943. .password("password")
  2944. .roles("USER")
  2945. .build();
  2946. JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
  2947. users.createUser(user);
  2948. return users;
  2949. }
  2950. }
  2951. ----
  2952. .Kotlin
  2953. [source,kotlin,role="secondary"]
  2954. ----
  2955. @Configuration
  2956. open class SecurityConfiguration {
  2957. @Bean
  2958. fun dataSource(): DataSource {
  2959. return EmbeddedDatabaseBuilder()
  2960. .setType(EmbeddedDatabaseType.H2)
  2961. .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
  2962. .build()
  2963. }
  2964. @Bean
  2965. fun users(val dataSource: DataSource): UserDetailsManager {
  2966. val user = User.withDefaultPasswordEncoder()
  2967. .username("user")
  2968. .password("password")
  2969. .roles("USER")
  2970. .build()
  2971. val users = JdbcUserDetailsManager(dataSource)
  2972. users.createUser(user)
  2973. return users
  2974. }
  2975. }
  2976. ----
  2977. ====
  2978. ===== In-Memory Authentication
  2979. If you are using `auth.inMemoryAuthentication()` for xref:servlet/authentication/passwords/in-memory.adoc[In-Memory Authentication support], you can replace:
  2980. ====
  2981. .Java
  2982. [source,java,role="primary"]
  2983. ----
  2984. @Configuration
  2985. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  2986. @Override
  2987. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  2988. UserDetails user = User.withDefaultPasswordEncoder()
  2989. .username("user")
  2990. .password("password")
  2991. .roles("USER")
  2992. .build();
  2993. auth.inMemoryAuthentication()
  2994. .withUser(user);
  2995. }
  2996. }
  2997. ----
  2998. .Kotlin
  2999. [source,kotlin,role="secondary"]
  3000. ----
  3001. @Configuration
  3002. open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
  3003. override fun configure(val auth: AuthenticationManagerBuilder) {
  3004. val user = User.withDefaultPasswordEncoder()
  3005. .username("user")
  3006. .password("password")
  3007. .roles("USER")
  3008. .build()
  3009. auth.inMemoryAuthentication()
  3010. .withUser(user)
  3011. }
  3012. }
  3013. ----
  3014. ====
  3015. with:
  3016. ====
  3017. .Java
  3018. [source,java,role="primary"]
  3019. ----
  3020. @Configuration
  3021. public class SecurityConfiguration {
  3022. @Bean
  3023. public InMemoryUserDetailsManager userDetailsService() {
  3024. UserDetails user = User.withDefaultPasswordEncoder()
  3025. .username("user")
  3026. .password("password")
  3027. .roles("USER")
  3028. .build();
  3029. return new InMemoryUserDetailsManager(user);
  3030. }
  3031. }
  3032. ----
  3033. .Kotlin
  3034. [source,kotlin,role="secondary"]
  3035. ----
  3036. @Configuration
  3037. open class SecurityConfiguration {
  3038. @Bean
  3039. fun userDetailsService(): InMemoryUserDetailsManager {
  3040. UserDetails user = User.withDefaultPasswordEncoder()
  3041. .username("user")
  3042. .password("password")
  3043. .roles("USER")
  3044. .build()
  3045. return InMemoryUserDetailsManager(user)
  3046. }
  3047. }
  3048. ----
  3049. ====
  3050. ===== Other Scenarios
  3051. If you are using `AuthenticationManagerBuilder` for something more sophisticated, you can xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[publish your own `AuthenticationManager` `@Bean`] or wire an `AuthenticationManager` instance into the `HttpSecurity` DSL with {security-api-url}org/springframework/security/config/annotation/web/builders/HttpSecurity.html#authenticationManager(org.springframework.security.authentication.AuthenticationManager)[`HttpSecurity#authenticationManager`].
  3052. ==== Publish a `WebSecurityCustomizer` Bean
  3053. Spring Security 5.4 https://github.com/spring-projects/spring-security/issues/8978[introduced `WebSecurityCustomizer`] to replace `configure(WebSecurity web)` in `WebSecurityConfigurerAdapter`.
  3054. To prepare for its removal, you can replace code like the following:
  3055. ====
  3056. .Java
  3057. [source,java,role="primary"]
  3058. ----
  3059. @Configuration
  3060. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  3061. @Override
  3062. public void configure(WebSecurity web) {
  3063. web.ignoring().antMatchers("/ignore1", "/ignore2");
  3064. }
  3065. }
  3066. ----
  3067. .Kotlin
  3068. [source,kotlin,role="secondary"]
  3069. ----
  3070. @Configuration
  3071. open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
  3072. override fun configure(val web: WebSecurity) {
  3073. web.ignoring().antMatchers("/ignore1", "/ignore2")
  3074. }
  3075. }
  3076. ----
  3077. ====
  3078. with:
  3079. ====
  3080. .Java
  3081. [source,java,role="primary"]
  3082. ----
  3083. @Configuration
  3084. public class SecurityConfiguration {
  3085. @Bean
  3086. public WebSecurityCustomizer webSecurityCustomizer() {
  3087. return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
  3088. }
  3089. }
  3090. ----
  3091. .Kotlin
  3092. [source,kotlin,role="secondary"]
  3093. ----
  3094. @Configuration
  3095. open class SecurityConfiguration {
  3096. @Bean
  3097. fun webSecurityCustomizer(): WebSecurityCustomizer {
  3098. return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2")
  3099. }
  3100. }
  3101. ----
  3102. ====
  3103. === Update Password Encoding
  3104. In 6.0, password encoding minimums are updated for PBKDF2, SCrypt, and Argon2.
  3105. [NOTE]
  3106. ====
  3107. If you are using the default password encoder, then there are no preparation steps to follow and this section can be skipped.
  3108. ====
  3109. ==== Update `Pbkdf2PasswordEncoder`
  3110. If you are xref:features/authentication/password-storage.adoc#authentication-password-storage-pbkdf2[using `Pbkdf2PasswordEncoder`], the constructors are replaced with static factories that refer to the Spring Security version that the given settings apply to.
  3111. ===== Replace Deprecated Constructor Usage
  3112. If you use the default constructor, you should begin by changing:
  3113. ====
  3114. .Java
  3115. [source,java,role="primary"]
  3116. ----
  3117. @Bean
  3118. PasswordEncoder passwordEncoder() {
  3119. return new Pbkdf2PasswordEncoder();
  3120. }
  3121. ----
  3122. .Kotlin
  3123. [source,kotlin,role="secondary"]
  3124. ----
  3125. @Bean
  3126. fun passwordEncoder(): PasswordEncoder {
  3127. return Pbkdf2PasswordEncoder()
  3128. }
  3129. ----
  3130. ====
  3131. to:
  3132. ====
  3133. .Java
  3134. [source,java,role="primary"]
  3135. ----
  3136. @Bean
  3137. PasswordEncoder passwordEncoder() {
  3138. return Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5();
  3139. }
  3140. ----
  3141. .Kotlin
  3142. [source,kotlin,role="secondary"]
  3143. ----
  3144. @Bean
  3145. fun passwordEncoder(): PasswordEncoder {
  3146. return Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()
  3147. }
  3148. ----
  3149. ====
  3150. Or, if you have custom settings, change to the constructor that specifies all settings, like so:
  3151. ====
  3152. .Java
  3153. [source,java,role="primary"]
  3154. ----
  3155. @Bean
  3156. PasswordEncoder passwordEncoder() {
  3157. PasswordEncoder current = new Pbkdf2PasswordEncoder("mysecret".getBytes(UTF_8), 320000);
  3158. return current;
  3159. }
  3160. ----
  3161. .Kotlin
  3162. [source,kotlin,role="secondary"]
  3163. ----
  3164. @Bean
  3165. fun passwordEncoder(): PasswordEncoder {
  3166. val current: PasswordEncoder = Pbkdf2PasswordEncoder("mysecret".getBytes(UTF_8), 320000)
  3167. return current
  3168. }
  3169. ----
  3170. ====
  3171. Change them to use the fully-specified constructor, like the following:
  3172. ====
  3173. .Java
  3174. [source,java,role="primary"]
  3175. ----
  3176. @Bean
  3177. PasswordEncoder passwordEncoder() {
  3178. PasswordEncoder current = new Pbkdf2PasswordEncoder("mysecret".getBytes(UTF_8), 16, 185000, 256);
  3179. return current;
  3180. }
  3181. ----
  3182. .Kotlin
  3183. [source,kotlin,role="secondary"]
  3184. ----
  3185. @Bean
  3186. fun passwordEncoder(): PasswordEncoder {
  3187. val current: PasswordEncoder = Pbkdf2PasswordEncoder("mysecret".getBytes(UTF_8), 16, 185000, 256)
  3188. return current
  3189. }
  3190. ----
  3191. ====
  3192. ===== Use `DelegatedPasswordEncoder`
  3193. Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatedPasswordEncoder`.
  3194. The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
  3195. ====
  3196. .Java
  3197. [source,java,role="primary"]
  3198. ----
  3199. @Bean
  3200. PasswordEncoder passwordEncoder() {
  3201. String prefix = "pbkdf2@5.8";
  3202. PasswordEncoder current = // ... see previous step
  3203. PasswordEncoder upgraded = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
  3204. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded));
  3205. delegating.setDefaultPasswordEncoderFormatches(current);
  3206. return delegating;
  3207. }
  3208. ----
  3209. .Kotlin
  3210. [source,kotlin,role="secondary"]
  3211. ----
  3212. @Bean
  3213. fun passwordEncoder(): PasswordEncoder {
  3214. String prefix = "pbkdf2@5.8"
  3215. PasswordEncoder current = // ... see previous step
  3216. PasswordEncoder upgraded = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
  3217. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded))
  3218. delegating.setDefaultPasswordEncoderFormatches(current)
  3219. return delegating
  3220. }
  3221. ----
  3222. ====
  3223. ==== Update `SCryptPasswordEncoder`
  3224. If you are xref:features/authentication/password-storage.adoc#authentication-password-storage-scrypt[using `SCryptPasswordEncoder`], the constructors are replaced with static factories that refer to the Spring Security version that the given settings apply to.
  3225. ===== Replace Deprecated Constructor Usage
  3226. If you use the default constructor, you should begin by changing:
  3227. ====
  3228. .Java
  3229. [source,java,role="primary"]
  3230. ----
  3231. @Bean
  3232. PasswordEncoder passwordEncoder() {
  3233. return new SCryptPasswordEncoder();
  3234. }
  3235. ----
  3236. .Kotlin
  3237. [source,kotlin,role="secondary"]
  3238. ----
  3239. @Bean
  3240. fun passwordEncoder(): PasswordEncoder {
  3241. return SCryptPasswordEncoder()
  3242. }
  3243. ----
  3244. ====
  3245. to:
  3246. ====
  3247. .Java
  3248. [source,java,role="primary"]
  3249. ----
  3250. @Bean
  3251. PasswordEncoder passwordEncoder() {
  3252. return SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
  3253. }
  3254. ----
  3255. .Kotlin
  3256. [source,kotlin,role="secondary"]
  3257. ----
  3258. @Bean
  3259. fun passwordEncoder(): PasswordEncoder {
  3260. return SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()
  3261. }
  3262. ----
  3263. ====
  3264. ===== Use `DelegatedPasswordEncoder`
  3265. Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatedPasswordEncoder`.
  3266. The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
  3267. ====
  3268. .Java
  3269. [source,java,role="primary"]
  3270. ----
  3271. @Bean
  3272. PasswordEncoder passwordEncoder() {
  3273. String prefix = "scrypt@5.8";
  3274. PasswordEncoder current = // ... see previous step
  3275. PasswordEncoder upgraded = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
  3276. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded));
  3277. delegating.setDefaultPasswordEncoderFormatches(current);
  3278. return delegating;
  3279. }
  3280. ----
  3281. .Kotlin
  3282. [source,kotlin,role="secondary"]
  3283. ----
  3284. @Bean
  3285. fun passwordEncoder(): PasswordEncoder {
  3286. String prefix = "scrypt@5.8"
  3287. PasswordEncoder current = // ... see previous step
  3288. PasswordEncoder upgraded = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
  3289. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded))
  3290. delegating.setDefaultPasswordEncoderFormatches(current)
  3291. return delegating
  3292. }
  3293. ----
  3294. ====
  3295. ==== Update `Argon2PasswordEncoder`
  3296. If you are xref:features/authentication/password-storage.adoc#authentication-password-storage-argon2[using `Argon2PasswordEncoder`], the constructors are replaced with static factories that refer to the Spring Security version that the given settings apply to.
  3297. ===== Replace Deprecated Constructor Usage
  3298. If you use the default constructor, you should begin by changing:
  3299. ====
  3300. .Java
  3301. [source,java,role="primary"]
  3302. ----
  3303. @Bean
  3304. PasswordEncoder passwordEncoder() {
  3305. return new Argon2PasswordEncoder();
  3306. }
  3307. ----
  3308. .Kotlin
  3309. [source,kotlin,role="secondary"]
  3310. ----
  3311. @Bean
  3312. fun passwordEncoder(): PasswordEncoder {
  3313. return Argon2PasswordEncoder()
  3314. }
  3315. ----
  3316. ====
  3317. to:
  3318. ====
  3319. .Java
  3320. [source,java,role="primary"]
  3321. ----
  3322. @Bean
  3323. PasswordEncoder passwordEncoder() {
  3324. return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();
  3325. }
  3326. ----
  3327. .Kotlin
  3328. [source,kotlin,role="secondary"]
  3329. ----
  3330. @Bean
  3331. fun passwordEncoder(): PasswordEncoder {
  3332. return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()
  3333. }
  3334. ----
  3335. ====
  3336. ===== Use `DelegatedPasswordEncoder`
  3337. Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatedPasswordEncoder`.
  3338. The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
  3339. ====
  3340. .Java
  3341. [source,java,role="primary"]
  3342. ----
  3343. @Bean
  3344. PasswordEncoder passwordEncoder() {
  3345. String prefix = "argon@5.8";
  3346. PasswordEncoder current = // ... see previous step
  3347. PasswordEncoder upgraded = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
  3348. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded));
  3349. delegating.setDefaultPasswordEncoderFormatches(current);
  3350. return delegating;
  3351. }
  3352. ----
  3353. .Kotlin
  3354. [source,kotlin,role="secondary"]
  3355. ----
  3356. @Bean
  3357. fun passwordEncoder(): PasswordEncoder {
  3358. String prefix = "argon@5.8"
  3359. PasswordEncoder current = // ... see previous step
  3360. PasswordEncoder upgraded = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
  3361. DelegatedPasswordEncoder delegating = new DelegatedPasswordEncoder(prefix, Map.of(prefix, upgraded))
  3362. delegating.setDefaultPasswordEncoderFormatches(current)
  3363. return delegating
  3364. }
  3365. ----
  3366. ====
  3367. === Deprecations in OAuth2 Client
  3368. In Spring Security 6, deprecated classes and methods were removed from xref:servlet/oauth2/client/index.adoc[OAuth2 Client].
  3369. Each deprecation is listed below, along with a direct replacement.
  3370. ==== `ServletOAuth2AuthorizedClientExchangeFilterFunction`
  3371. The method `setAccessTokenExpiresSkew(...)` can be replaced with one of:
  3372. * `ClientCredentialsOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3373. * `RefreshTokenOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3374. * `JwtBearerOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3375. The method `setClientCredentialsTokenResponseClient(...)` can be replaced with the constructor `ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager)`.
  3376. [NOTE]
  3377. ====
  3378. See xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-client-creds-grant[Client Credentials] for more information.
  3379. ====
  3380. ==== `OidcUserInfo`
  3381. The method `phoneNumberVerified(String)` can be replaced with `phoneNumberVerified(Boolean)`.
  3382. ==== `OAuth2AuthorizedClientArgumentResolver`
  3383. The method `setClientCredentialsTokenResponseClient(...)` can be replaced with the constructor `OAuth2AuthorizedClientArgumentResolver(OAuth2AuthorizedClientManager)`.
  3384. [NOTE]
  3385. ====
  3386. See xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-client-creds-grant[Client Credentials] for more information.
  3387. ====
  3388. ==== `ClaimAccessor`
  3389. The method `containsClaim(...)` can be replaced with `hasClaim(...)`.
  3390. ==== `OidcClientInitiatedLogoutSuccessHandler`
  3391. The method `setPostLogoutRedirectUri(URI)` can be replaced with `setPostLogoutRedirectUri(String)`.
  3392. ==== `HttpSessionOAuth2AuthorizationRequestRepository`
  3393. The method `setAllowMultipleAuthorizationRequests(...)` has no direct replacement.
  3394. ==== `AuthorizationRequestRepository`
  3395. The method `removeAuthorizationRequest(HttpServletRequest)` can be replaced with `removeAuthorizationRequest(HttpServletRequest, HttpServletResponse)`.
  3396. ==== `ClientRegistration`
  3397. The method `getRedirectUriTemplate()` can be replaced with `getRedirectUri()`.
  3398. ==== `ClientRegistration.Builder`
  3399. The method `redirectUriTemplate(...)` can be replaced with `redirectUri(...)`.
  3400. ==== `AbstractOAuth2AuthorizationGrantRequest`
  3401. The constructor `AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType)` can be replaced with `AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType, ClientRegistration)`.
  3402. ==== `ClientAuthenticationMethod`
  3403. The static field `BASIC` can be replaced with `CLIENT_SECRET_BASIC`.
  3404. The static field `POST` can be replaced with `CLIENT_SECRET_POST`.
  3405. ==== `OAuth2AccessTokenResponseHttpMessageConverter`
  3406. The field `tokenResponseConverter` has no direct replacement.
  3407. The method `setTokenResponseConverter(...)` can be replaced with `setAccessTokenResponseConverter(...)`.
  3408. The field `tokenResponseParametersConverter` has no direct replacement.
  3409. The method `setTokenResponseParametersConverter(...)` can be replaced with `setAccessTokenResponseParametersConverter(...)`.
  3410. ==== `NimbusAuthorizationCodeTokenResponseClient`
  3411. The class `NimbusAuthorizationCodeTokenResponseClient` can be replaced with `DefaultAuthorizationCodeTokenResponseClient`.
  3412. ==== `NimbusJwtDecoderJwkSupport`
  3413. The class `NimbusJwtDecoderJwkSupport` can be replaced with `NimbusJwtDecoder` or `JwtDecoders`.
  3414. ==== `ImplicitGrantConfigurer`
  3415. The class `ImplicitGrantConfigurer` has no direct replacement.
  3416. [WARNING]
  3417. ====
  3418. Use of the `implicit` grant type is not recommended and all related support is removed in Spring Security 6.
  3419. ====
  3420. ==== `AuthorizationGrantType`
  3421. The static field `IMPLICIT` has no direct replacement.
  3422. [WARNING]
  3423. ====
  3424. Use of the `implicit` grant type is not recommended and all related support is removed in Spring Security 6.
  3425. ====
  3426. ==== `OAuth2AuthorizationResponseType`
  3427. The static field `TOKEN` has no direct replacement.
  3428. [WARNING]
  3429. ====
  3430. Use of the `implicit` grant type is not recommended and all related support is removed in Spring Security 6.
  3431. ====
  3432. ==== `OAuth2AuthorizationRequest`
  3433. The static method `implicit()` has no direct replacement.
  3434. [WARNING]
  3435. ====
  3436. Use of the `implicit` grant type is not recommended and all related support is removed in Spring Security 6.
  3437. ====
  3438. === Deprecations in OAuth2 Resource Server
  3439. In Spring Security 6, deprecated classes and methods were removed from xref:servlet/oauth2/resource-server/index.adoc[OAuth2 Resource Server].
  3440. Each deprecation is listed below, along with a direct replacement.
  3441. ==== `JwtAuthenticationConverter`
  3442. The method `extractAuthorities(...)` can be replaced with `JwtGrantedAuthoritiesConverter#convert(...)`.
  3443. == Reactive
  3444. === Use `AuthorizationManager` for Method Security
  3445. 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.
  3446. Should you run into trouble with making these changes, you can follow the
  3447. <<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
  3448. 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.
  3449. [[reactive-change-to-useauthorizationmanager]]
  3450. ==== Change `useAuthorizationManager` to `true`
  3451. To opt in, change `useAuthorizationManager` to `true` like so:
  3452. ====
  3453. .Java
  3454. [source,java,role="primary"]
  3455. ----
  3456. @EnableReactiveMethodSecurity
  3457. ----
  3458. .Kotlin
  3459. [source,kotlin,role="secondary"]
  3460. ----
  3461. @EnableReactiveMethodSecurity
  3462. ----
  3463. ====
  3464. changes to:
  3465. ====
  3466. .Java
  3467. [source,java,role="primary"]
  3468. ----
  3469. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  3470. ----
  3471. .Kotlin
  3472. [source,kotlin,role="secondary"]
  3473. ----
  3474. @EnableReactiveMethodSecurity(useAuthorizationManager = true)
  3475. ----
  3476. ====
  3477. [[reactive-check-for-annotationconfigurationexceptions]]
  3478. ==== Check for ``AnnotationConfigurationException``s
  3479. `useAuthorizationManager` activates stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  3480. 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.
  3481. [[reactive-authorizationmanager-methods-opt-out]]
  3482. ==== Opt-out Steps
  3483. If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
  3484. ====
  3485. .Java
  3486. [source,java,role="primary"]
  3487. ----
  3488. @EnableReactiveMethodSecurity
  3489. ----
  3490. .Kotlin
  3491. [source,kotlin,role="secondary"]
  3492. ----
  3493. @EnableReactiveMethodSecurity
  3494. ----
  3495. ====
  3496. to:
  3497. ====
  3498. .Java
  3499. [source,java,role="primary"]
  3500. ----
  3501. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  3502. ----
  3503. .Kotlin
  3504. [source,kotlin,role="secondary"]
  3505. ----
  3506. @EnableReactiveMethodSecurity(useAuthorizationManager = false)
  3507. ----
  3508. ====
  3509. === Propagate ``AuthenticationServiceException``s
  3510. {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`].
  3511. 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.
  3512. ==== Configure `ServerAuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
  3513. To prepare for the 6.0 default, `httpBasic` and `oauth2ResourceServer` should be configured to rethrow ``AuthenticationServiceException``s.
  3514. For each, construct the appropriate authentication entry point for `httpBasic` and for `oauth2ResourceServer`:
  3515. ====
  3516. .Java
  3517. [source,java,role="primary"]
  3518. ----
  3519. ServerAuthenticationEntryPoint bearerEntryPoint = new BearerTokenServerAuthenticationEntryPoint();
  3520. ServerAuthenticationEntryPoint basicEntryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);
  3521. ----
  3522. .Kotlin
  3523. [source,kotlin,role="secondary"]
  3524. ----
  3525. val bearerEntryPoint: ServerAuthenticationEntryPoint = BearerTokenServerAuthenticationEntryPoint()
  3526. val basicEntryPoint: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
  3527. ----
  3528. ====
  3529. [NOTE]
  3530. ====
  3531. If you use a custom `AuthenticationEntryPoint` for either or both mechanisms, use that one instead for the remaining steps.
  3532. ====
  3533. Then, construct and configure a `ServerAuthenticationEntryPointFailureHandler` for each one:
  3534. ====
  3535. .Java
  3536. [source,java,role="primary"]
  3537. ----
  3538. AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
  3539. bearerFailureHandler.setRethrowAuthenticationServiceException(true);
  3540. AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
  3541. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  3542. ----
  3543. .Kotlin
  3544. [source,kotlin,role="secondary"]
  3545. ----
  3546. val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
  3547. bearerFailureHandler.setRethrowAuthenticationServiceException(true)
  3548. val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
  3549. basicFailureHandler.setRethrowAuthenticationServiceException(true)
  3550. ----
  3551. ====
  3552. Finally, wire each authentication failure handler into the DSL, like so:
  3553. ====
  3554. .Java
  3555. [source,java,role="primary"]
  3556. ----
  3557. http
  3558. .httpBasic((basic) -> basic.authenticationFailureHandler(basicFailureHandler))
  3559. .oauth2ResourceServer((oauth2) -> oauth2.authenticationFailureHandler(bearerFailureHandler))
  3560. ----
  3561. .Kotlin
  3562. [source,kotlin,role="secondary"]
  3563. ----
  3564. http {
  3565. httpBasic {
  3566. authenticationFailureHandler = basicFailureHandler
  3567. }
  3568. oauth2ResourceServer {
  3569. authenticationFailureHandler = bearerFailureHandler
  3570. }
  3571. }
  3572. ----
  3573. ====
  3574. [[reactive-authenticationfailurehandler-opt-out]]
  3575. ==== Opt-out Steps
  3576. 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.
  3577. === Deprecations in OAuth2 Client
  3578. ==== `ServerOAuth2AuthorizedClientExchangeFilterFunction`
  3579. The method `setAccessTokenExpiresSkew(...)` can be replaced with one of:
  3580. * `ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3581. * `RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3582. * `JwtBearerReactiveOAuth2AuthorizedClientProvider#setClockSkew(...)`
  3583. The method `setClientCredentialsTokenResponseClient(...)` can be replaced with the constructor `ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)`.
  3584. [NOTE]
  3585. ====
  3586. See xref:reactive/oauth2/client/authorization-grants.adoc#oauth2Client-client-creds-grant[Client Credentials] for more information.
  3587. ====
  3588. ==== `WebSessionOAuth2ServerAuthorizationRequestRepository`
  3589. The method `setAllowMultipleAuthorizationRequests(...)` has no direct replacement.
  3590. ==== `UnAuthenticatedServerOAuth2AuthorizedClientRepository`
  3591. The class `UnAuthenticatedServerOAuth2AuthorizedClientRepository` has no direct replacement. Usage of the class can be replaced with `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager`.