authorization.adoc 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559
  1. = Authorization Migrations
  2. The following steps relate to changes around how authorization is performed.
  3. == Use `AuthorizationManager` for Method Security
  4. 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.
  5. 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.
  6. [[servlet-replace-globalmethodsecurity-with-methodsecurity]]
  7. === 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]
  8. {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.
  9. 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.
  10. This means that the following two listings are functionally equivalent:
  11. ====
  12. .Java
  13. [source,java,role="primary"]
  14. ----
  15. @EnableGlobalMethodSecurity(prePostEnabled = true)
  16. ----
  17. .Kotlin
  18. [source,kotlin,role="secondary"]
  19. ----
  20. @EnableGlobalMethodSecurity(prePostEnabled = true)
  21. ----
  22. .Xml
  23. [source,xml,role="secondary"]
  24. ----
  25. <global-method-security pre-post-enabled="true"/>
  26. ----
  27. ====
  28. and:
  29. ====
  30. .Java
  31. [source,java,role="primary"]
  32. ----
  33. @EnableMethodSecurity
  34. ----
  35. .Kotlin
  36. [source,kotlin,role="secondary"]
  37. ----
  38. @EnableMethodSecurity
  39. ----
  40. .Xml
  41. [source,xml,role="secondary"]
  42. ----
  43. <method-security/>
  44. ----
  45. ====
  46. For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.
  47. For example, a listing like:
  48. ====
  49. .Java
  50. [source,java,role="primary"]
  51. ----
  52. @EnableGlobalMethodSecurity(securedEnabled = true)
  53. ----
  54. .Kotlin
  55. [source,kotlin,role="secondary"]
  56. ----
  57. @EnableGlobalMethodSecurity(securedEnabled = true)
  58. ----
  59. .Xml
  60. [source,xml,role="secondary"]
  61. ----
  62. <global-method-security secured-enabled="true"/>
  63. ----
  64. ====
  65. should change to:
  66. ====
  67. .Java
  68. [source,java,role="primary"]
  69. ----
  70. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  71. ----
  72. .Kotlin
  73. [source,kotlin,role="secondary"]
  74. ----
  75. @EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
  76. ----
  77. .Xml
  78. [source,xml,role="secondary"]
  79. ----
  80. <method-security secured-enabled="true" pre-post-enabled="false"/>
  81. ----
  82. ====
  83. [[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
  84. === Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
  85. `@EnableMethodSecurity` does not pick up a `PermissionEvaluator`.
  86. This helps keep its API simple.
  87. If you have a custom {security-api-url}org/springframework/security/access/PermissionEvaluator.html[`PermissionEvaluator`] `@Bean`, please change it from:
  88. ====
  89. .Java
  90. [source,java,role="primary"]
  91. ----
  92. @Bean
  93. static PermissionEvaluator permissionEvaluator() {
  94. // ... your evaluator
  95. }
  96. ----
  97. .Kotlin
  98. [source,kotlin,role="secondary"]
  99. ----
  100. companion object {
  101. @Bean
  102. fun permissionEvaluator(): PermissionEvaluator {
  103. // ... your evaluator
  104. }
  105. }
  106. ----
  107. ====
  108. to:
  109. ====
  110. .Java
  111. [source,java,role="primary"]
  112. ----
  113. @Bean
  114. static MethodSecurityExpressionHandler expressionHandler() {
  115. var expressionHandler = new DefaultMethodSecurityExpressionHandler();
  116. expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
  117. return expressionHandler;
  118. }
  119. ----
  120. .Kotlin
  121. [source,kotlin,role="secondary"]
  122. ----
  123. companion object {
  124. @Bean
  125. fun expressionHandler(): MethodSecurityExpressionHandler {
  126. val expressionHandler = DefaultMethodSecurityExpressionHandler
  127. expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
  128. return expressionHandler
  129. }
  130. }
  131. ----
  132. ====
  133. === Replace any custom method-security ``AccessDecisionManager``s
  134. 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.
  135. The preparation strategy will depend on your reason for each arrangement.
  136. Read on to find the best match for your situation.
  137. ==== I use `UnanimousBased`
  138. 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`].
  139. However, if you do discover that you cannot accept the default authorization managers, you can use `AuthorizationManagers.allOf` to compose your own arrangement.
  140. 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`].
  141. ==== I use `AffirmativeBased`
  142. 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:
  143. ====
  144. .Java
  145. [source,java,role="primary"]
  146. ----
  147. AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
  148. // ... your list of authorization managers
  149. )
  150. ----
  151. .Kotlin
  152. [source,kotlin,role="secondary"]
  153. ----
  154. val authorization = AuthorizationManagers.anyOf(
  155. // ... your list of authorization managers
  156. )
  157. ----
  158. ====
  159. 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`].
  160. ==== I use `ConsensusBased`
  161. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  162. 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.
  163. 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`].
  164. ==== I use a custom `AccessDecisionVoter`
  165. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  166. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  167. 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:
  168. ====
  169. .Java
  170. [source,java,role="primary"]
  171. ----
  172. public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
  173. private final SecurityMetadataSource metadata;
  174. private final AccessDecisionVoter voter;
  175. public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
  176. ExpressionBasedAnnotationAttributeFactory attributeFactory =
  177. new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
  178. this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
  179. ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
  180. expressionAdvice.setExpressionHandler(expressionHandler);
  181. this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
  182. }
  183. public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
  184. List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
  185. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  186. if (decision == ACCESS_GRANTED) {
  187. return new AuthorizationDecision(true);
  188. }
  189. if (decision == ACCESS_DENIED) {
  190. return new AuthorizationDecision(false);
  191. }
  192. return null; // abstain
  193. }
  194. }
  195. ----
  196. ====
  197. 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`].
  198. ==== I use a custom `AfterInvocationManager`
  199. {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`].
  200. The difference is that `AuthorizationManager<MethodInvocation>` replaces `AccessDecisionManager` and `AuthorizationManager<MethodInvocationResult>` replaces `AfterInvocationManager`.
  201. 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`.
  202. ==== I use `RunAsManager`
  203. There is currently https://github.com/spring-projects/spring-security/issues/11331[no replacement for `RunAsManager`] though one is being considered.
  204. It is quite straightforward to adapt a `RunAsManager`, though, to the `AuthorizationManager` API, if needed.
  205. Here is some pseudocode to get you started:
  206. ====
  207. .Java
  208. [source,java,role="primary"]
  209. ----
  210. public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
  211. private final RunAsManager runAs = new RunAsManagerImpl();
  212. private final SecurityMetadataSource metadata;
  213. private final AuthorizationManager<T> authorization;
  214. // ... constructor
  215. public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
  216. Supplier<Authentication> wrapped = (auth) -> {
  217. List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
  218. return this.runAs.buildRunAs(auth, object, attributes);
  219. };
  220. return this.authorization.check(wrapped, object);
  221. }
  222. }
  223. ----
  224. ====
  225. 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`].
  226. [[servlet-check-for-annotationconfigurationexceptions]]
  227. === Check for ``AnnotationConfigurationException``s
  228. `@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
  229. 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.
  230. == Use `AuthorizationManager` for Message Security
  231. 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.
  232. 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.
  233. === Ensure all messages have defined authorization rules
  234. The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
  235. xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
  236. To prepare for this, ensure that authorization rules exist are declared for every request.
  237. For example, an application configuration like:
  238. ====
  239. .Java
  240. [source,java,role="primary"]
  241. ----
  242. @Override
  243. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  244. messages
  245. .simpDestMatchers("/user/queue/errors").permitAll()
  246. .simpDestMatchers("/admin/**").hasRole("ADMIN");
  247. }
  248. ----
  249. .Kotlin
  250. [source,kotlin,role="secondary"]
  251. ----
  252. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  253. messages
  254. .simpDestMatchers("/user/queue/errors").permitAll()
  255. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  256. }
  257. ----
  258. .Xml
  259. [source,xml,role="secondary"]
  260. ----
  261. <websocket-message-broker>
  262. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  263. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  264. </websocket-message-broker>
  265. ----
  266. ====
  267. should change to:
  268. ====
  269. .Java
  270. [source,java,role="primary"]
  271. ----
  272. @Override
  273. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  274. messages
  275. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  276. .simpDestMatchers("/user/queue/errors").permitAll()
  277. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  278. .anyMessage().denyAll();
  279. }
  280. ----
  281. .Kotlin
  282. [source,kotlin,role="secondary"]
  283. ----
  284. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  285. messages
  286. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  287. .simpDestMatchers("/user/queue/errors").permitAll()
  288. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  289. .anyMessage().denyAll()
  290. }
  291. ----
  292. .Xml
  293. [source,xml,role="secondary"]
  294. ----
  295. <websocket-message-broker>
  296. <intercept-message type="CONNECT" access="permitAll"/>
  297. <intercept-message type="DISCONNECT" access="permitAll"/>
  298. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  299. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  300. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  301. <intercept-message pattern="/**" access="denyAll"/>
  302. </websocket-message-broker>
  303. ----
  304. ====
  305. === Add `@EnableWebSocketSecurity`
  306. [NOTE]
  307. ====
  308. If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
  309. Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
  310. Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
  311. ====
  312. If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
  313. For example, you can add it to your websocket security configuration class, like so:
  314. ====
  315. .Java
  316. [source,java,role="primary"]
  317. ----
  318. @EnableWebSocketSecurity
  319. @Configuration
  320. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  321. // ...
  322. }
  323. ----
  324. .Kotlin
  325. [source,kotlin,role="secondary"]
  326. ----
  327. @EnableWebSocketSecurity
  328. @Configuration
  329. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  330. // ...
  331. }
  332. ----
  333. ====
  334. This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
  335. === Use an `AuthorizationManager<Message<?>>` instance
  336. To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
  337. For example, the following application configuration:
  338. ====
  339. .Java
  340. [source,java,role="primary"]
  341. ----
  342. @Override
  343. protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
  344. messages
  345. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  346. .simpDestMatchers("/user/queue/errors").permitAll()
  347. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  348. .anyMessage().denyAll();
  349. }
  350. ----
  351. .Kotlin
  352. [source,kotlin,role="secondary"]
  353. ----
  354. override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
  355. messages
  356. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  357. .simpDestMatchers("/user/queue/errors").permitAll()
  358. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  359. .anyMessage().denyAll()
  360. }
  361. ----
  362. .Xml
  363. [source,xml,role="secondary"]
  364. ----
  365. <websocket-message-broker>
  366. <intercept-message type="CONNECT" access="permitAll"/>
  367. <intercept-message type="DISCONNECT" access="permitAll"/>
  368. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  369. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  370. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  371. <intercept-message pattern="/**" access="denyAll"/>
  372. </websocket-message-broker>
  373. ----
  374. ====
  375. changes to:
  376. ====
  377. .Java
  378. [source,java,role="primary"]
  379. ----
  380. @Bean
  381. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  382. messages
  383. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  384. .simpDestMatchers("/user/queue/errors").permitAll()
  385. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  386. .anyMessage().denyAll();
  387. return messages.build();
  388. }
  389. ----
  390. .Kotlin
  391. [source,kotlin,role="secondary"]
  392. ----
  393. @Bean
  394. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  395. messages
  396. .simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
  397. .simpDestMatchers("/user/queue/errors").permitAll()
  398. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  399. .anyMessage().denyAll()
  400. return messages.build()
  401. }
  402. ----
  403. .Xml
  404. [source,xml,role="secondary"]
  405. ----
  406. <websocket-message-broker use-authorization-manager="true">
  407. <intercept-message type="CONNECT" access="permitAll"/>
  408. <intercept-message type="DISCONNECT" access="permitAll"/>
  409. <intercept-message type="UNSUBSCRIBE" access="permitAll"/>
  410. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  411. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  412. <intercept-message pattern="/**" access="denyAll"/>
  413. </websocket-message-broker>
  414. ----
  415. ====
  416. === Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
  417. If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
  418. For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
  419. ====
  420. .Java
  421. [source,java,role="primary"]
  422. ----
  423. @EnableWebSocketSecurity
  424. @Configuration
  425. public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
  426. // ...
  427. }
  428. ----
  429. .Kotlin
  430. [source,kotlin,role="secondary"]
  431. ----
  432. @EnableWebSocketSecurity
  433. @Configuration
  434. class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
  435. // ...
  436. }
  437. ----
  438. ====
  439. changes to:
  440. ====
  441. .Java
  442. [source,java,role="primary"]
  443. ----
  444. @EnableWebSocketSecurity
  445. @Configuration
  446. public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
  447. // ...
  448. }
  449. ----
  450. .Kotlin
  451. [source,kotlin,role="secondary"]
  452. ----
  453. @EnableWebSocketSecurity
  454. @Configuration
  455. class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
  456. // ...
  457. }
  458. ----
  459. ====
  460. [[servlet-authorizationmanager-messages-opt-out]]
  461. === Opt-out Steps
  462. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  463. ==== I cannot declare an authorization rule for all requests
  464. 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:
  465. ====
  466. .Java
  467. [source,java,role="primary"]
  468. ----
  469. @Bean
  470. AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
  471. messages
  472. .simpDestMatchers("/user/queue/errors").permitAll()
  473. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  474. // ...
  475. .anyMessage().permitAll();
  476. return messages.build();
  477. }
  478. ----
  479. .Kotlin
  480. [source,kotlin,role="secondary"]
  481. ----
  482. @Bean
  483. fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
  484. messages
  485. .simpDestMatchers("/user/queue/errors").permitAll()
  486. .simpDestMatchers("/admin/**").hasRole("ADMIN")
  487. // ...
  488. .anyMessage().permitAll();
  489. return messages.build()
  490. }
  491. ----
  492. .Xml
  493. [source,xml,role="secondary"]
  494. ----
  495. <websocket-message-broker use-authorization-manager="true">
  496. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  497. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  498. <!-- ... -->
  499. <intercept-message pattern="/**" access="permitAll"/>
  500. </websocket-message-broker>
  501. ----
  502. ====
  503. ==== I cannot get CSRF working, need some other `AbstractSecurityWebSocketMessageBrokerConfigurer` feature, or am having trouble with `AuthorizationManager`
  504. In the case of Java, you may continue using `AbstractMessageSecurityWebSocketMessageBrokerConfigurer`.
  505. Even though it is deprecated, it will not be removed in 6.0.
  506. In the case of XML, you can opt out of `AuthorizationManager` by setting `use-authorization-manager="false"`:
  507. ====
  508. .Xml
  509. [source,xml,role="secondary"]
  510. ----
  511. <websocket-message-broker>
  512. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  513. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  514. </websocket-message-broker>
  515. ----
  516. ====
  517. to:
  518. ====
  519. .Xml
  520. [source,xml,role="secondary"]
  521. ----
  522. <websocket-message-broker use-authorization-manager="false">
  523. <intercept-message pattern="/user/queue/errors" access="permitAll"/>
  524. <intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
  525. </websocket-message-broker>
  526. ----
  527. ====
  528. == Use `AuthorizationManager` for Request Security
  529. 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].
  530. 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.
  531. === Ensure that all requests have defined authorization rules
  532. In Spring Security 5.8 and earlier, requests with no authorization rule are permitted by default.
  533. It is a stronger security position to deny by default, thus requiring that authorization rules be clearly defined for every endpoint.
  534. As such, in 6.0, Spring Security by default denies any request that is missing an authorization rule.
  535. 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.
  536. 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.
  537. [NOTE]
  538. ====
  539. You may already have an `anyRequest` rule defined that you are happy with in which case this step can be skipped.
  540. ====
  541. Adding `denyAll` to the end looks like changing:
  542. ====
  543. .Java
  544. [source,java,role="primary"]
  545. ----
  546. http
  547. .authorizeRequests((authorize) -> authorize
  548. .filterSecurityInterceptorOncePerRequest(true)
  549. .mvcMatchers("/app/**").hasRole("APP")
  550. // ...
  551. )
  552. // ...
  553. ----
  554. .Kotlin
  555. [source,kotlin,role="secondary"]
  556. ----
  557. http {
  558. authorizeRequests {
  559. filterSecurityInterceptorOncePerRequest = true
  560. authorize("/app/**", hasRole("APP"))
  561. // ...
  562. }
  563. }
  564. ----
  565. .Xml
  566. [source,xml,role="secondary"]
  567. ----
  568. <http once-per-request="true">
  569. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  570. <!-- ... -->
  571. </http>
  572. ----
  573. ====
  574. to:
  575. ====
  576. .Java
  577. [source,java,role="primary"]
  578. ----
  579. http
  580. .authorizeRequests((authorize) -> authorize
  581. .filterSecurityInterceptorOncePerRequest(true)
  582. .mvcMatchers("/app/**").hasRole("APP")
  583. // ...
  584. .anyRequest().denyAll()
  585. )
  586. // ...
  587. ----
  588. .Kotlin
  589. [source,kotlin,role="secondary"]
  590. ----
  591. http {
  592. authorizeRequests {
  593. filterSecurityInterceptorOncePerRequest = true
  594. authorize("/app/**", hasRole("APP"))
  595. // ...
  596. authorize(anyRequest, denyAll)
  597. }
  598. }
  599. ----
  600. .Xml
  601. [source,xml,role="secondary"]
  602. ----
  603. <http once-per-request="true">
  604. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  605. <!-- ... -->
  606. <intercept-url pattern="/**" access="denyAll"/>
  607. </http>
  608. ----
  609. ====
  610. If you have already migrated to `authorizeHttpRequests`, the recommended change is the same.
  611. === Switch to `AuthorizationManager`
  612. 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.
  613. Change:
  614. ====
  615. .Java
  616. [source,java,role="primary"]
  617. ----
  618. http
  619. .authorizeRequests((authorize) -> authorize
  620. .filterSecurityInterceptorOncePerRequest(true)
  621. .mvcMatchers("/app/**").hasRole("APP")
  622. // ...
  623. .anyRequest().denyAll()
  624. )
  625. // ...
  626. ----
  627. .Kotlin
  628. [source,kotlin,role="secondary"]
  629. ----
  630. http {
  631. authorizeRequests {
  632. filterSecurityInterceptorOncePerRequest = true
  633. authorize("/app/**", hasRole("APP"))
  634. // ...
  635. authorize(anyRequest, denyAll)
  636. }
  637. }
  638. ----
  639. .Xml
  640. [source,xml,role="secondary"]
  641. ----
  642. <http once-per-request="true">
  643. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  644. <!-- ... -->
  645. <intercept-url pattern="/**" access="denyAll"/>
  646. </http>
  647. ----
  648. ====
  649. to:
  650. ====
  651. .Java
  652. [source,java,role="primary"]
  653. ----
  654. http
  655. .authorizeHttpRequests((authorize) -> authorize
  656. .shouldFilterAllDispatcherTypes(false)
  657. .mvcMatchers("/app/**").hasRole("APP")
  658. // ...
  659. .anyRequest().denyAll()
  660. )
  661. // ...
  662. ----
  663. .Kotlin
  664. [source,kotlin,role="secondary"]
  665. ----
  666. http {
  667. authorizeHttpRequests {
  668. shouldFilterAllDispatcherTypes = false
  669. authorize("/app/**", hasRole("APP"))
  670. // ...
  671. authorize(anyRequest, denyAll)
  672. }
  673. }
  674. ----
  675. .Xml
  676. [source,xml,role="secondary"]
  677. ----
  678. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  679. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  680. <!-- ... -->
  681. <intercept-url pattern="/**" access="denyAll"/>
  682. </http>
  683. ----
  684. ====
  685. === Migrate SpEL expressions to `AuthorizationManager`
  686. For authorization rules, Java tends to be easier to test and maintain than SpEL.
  687. As such, `authorizeHttpRequests` does not have a method for declaring a `String` SpEL.
  688. Instead, you can implement your own `AuthorizationManager` implementation or use `WebExpressionAuthorizationManager`.
  689. For completeness, both options will be demonstrated.
  690. First, if you have the following SpEL:
  691. ====
  692. .Java
  693. [source,java,role="primary"]
  694. ----
  695. http
  696. .authorizeRequests((authorize) -> authorize
  697. .filterSecurityInterceptorOncePerRequest(true)
  698. .mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  699. // ...
  700. .anyRequest().denyAll()
  701. )
  702. // ...
  703. ----
  704. .Kotlin
  705. [source,kotlin,role="secondary"]
  706. ----
  707. http {
  708. authorizeRequests {
  709. filterSecurityInterceptorOncePerRequest = true
  710. authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  711. // ...
  712. authorize(anyRequest, denyAll)
  713. }
  714. }
  715. ----
  716. ====
  717. Then you can compose your own `AuthorizationManager` with Spring Security authorization primitives like so:
  718. ====
  719. .Java
  720. [source,java,role="primary"]
  721. ----
  722. http
  723. .authorizeHttpRequests((authorize) -> authorize
  724. .shouldFilterAllDispatcherTypes(false)
  725. .mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  726. // ...
  727. .anyRequest().denyAll()
  728. )
  729. // ...
  730. ----
  731. .Kotlin
  732. [source,kotlin,role="secondary"]
  733. ----
  734. http {
  735. authorizeHttpRequests {
  736. shouldFilterAllDispatcherTypes = false
  737. authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
  738. // ...
  739. authorize(anyRequest, denyAll)
  740. }
  741. }
  742. ----
  743. ====
  744. Or you can use `WebExpressionAuthorizationManager` in the following way:
  745. ====
  746. .Java
  747. [source,java,role="primary"]
  748. ----
  749. http
  750. .authorizeRequests((authorize) -> authorize
  751. .filterSecurityInterceptorOncePerRequest(true)
  752. .mvcMatchers("/complicated/**").access(
  753. new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
  754. )
  755. // ...
  756. .anyRequest().denyAll()
  757. )
  758. // ...
  759. ----
  760. .Kotlin
  761. [source,kotlin,role="secondary"]
  762. ----
  763. http {
  764. authorizeRequests {
  765. filterSecurityInterceptorOncePerRequest = true
  766. authorize("/complicated/**", access(
  767. WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
  768. )
  769. // ...
  770. authorize(anyRequest, denyAll)
  771. }
  772. }
  773. ----
  774. ====
  775. [[switch-filter-all-dispatcher-types]]
  776. === Switch to filter all dispatcher types
  777. Spring Security 5.8 and earlier only xref:servlet/authorization/architecture.adoc[perform authorization] once per request.
  778. This means that dispatcher types like `FORWARD` and `INCLUDE` that run after `REQUEST` are not secured by default.
  779. It's recommended that Spring Security secure all dispatch types.
  780. As such, in 6.0, Spring Security changes this default.
  781. So, finally, change your authorization rules to filter all dispatcher types.
  782. To do this, you should change:
  783. ====
  784. .Java
  785. [source,java,role="primary"]
  786. ----
  787. http
  788. .authorizeHttpRequests((authorize) -> authorize
  789. .shouldFilterAllDispatcherTypes(false)
  790. .mvcMatchers("/app/**").hasRole("APP")
  791. // ...
  792. .anyRequest().denyAll()
  793. )
  794. // ...
  795. ----
  796. .Kotlin
  797. [source,kotlin,role="secondary"]
  798. ----
  799. http {
  800. authorizeHttpRequests {
  801. shouldFilterAllDispatcherTypes = false
  802. authorize("/app/**", hasRole("APP"))
  803. // ...
  804. authorize(anyRequest, denyAll)
  805. }
  806. }
  807. ----
  808. .Xml
  809. [source,xml,role="secondary"]
  810. ----
  811. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  812. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  813. <!-- ... -->
  814. <intercept-url pattern="/**" access="denyAll"/>
  815. </http>
  816. ----
  817. ====
  818. to:
  819. ====
  820. .Java
  821. [source,java,role="primary"]
  822. ----
  823. http
  824. .authorizeHttpRequests((authorize) -> authorize
  825. .shouldFilterAllDispatcherTypes(true)
  826. .mvcMatchers("/app/**").hasRole("APP")
  827. // ...
  828. .anyRequest().denyAll()
  829. )
  830. // ...
  831. ----
  832. .Kotlin
  833. [source,kotlin,role="secondary"]
  834. ----
  835. http {
  836. authorizeHttpRequests {
  837. shouldFilterAllDispatcherTypes = true
  838. authorize("/app/**", hasRole("APP"))
  839. // ...
  840. authorize(anyRequest, denyAll)
  841. }
  842. }
  843. ----
  844. .Xml
  845. [source,xml,role="secondary"]
  846. ----
  847. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  848. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  849. <!-- ... -->
  850. <intercept-url pattern="/**" access="denyAll"/>
  851. </http>
  852. ----
  853. ====
  854. And, the `FilterChainProxy` should be registered for all dispatcher types as well.
  855. 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:
  856. ====
  857. .application.properties
  858. [source,properties,role="primary"]
  859. ----
  860. spring.security.filter.dispatcher-types=request,async,error,forward,include
  861. ----
  862. ====
  863. If you are xref:servlet/configuration/java.adoc#_abstractsecuritywebapplicationinitializer[using the `AbstractSecurityWebApplicationInitializer`] you should override the `getSecurityDispatcherTypes` method and return all dispatcher types:
  864. ====
  865. .Java
  866. [source,java,role="primary"]
  867. ----
  868. import org.springframework.security.web.context.*;
  869. public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
  870. @Override
  871. protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
  872. return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.FORWARD,
  873. DispatcherType.FORWARD, DispatcherType.INCLUDE);
  874. }
  875. }
  876. ----
  877. ====
  878. ==== Permit `FORWARD` when using Spring MVC
  879. 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.
  880. This is because when Spring MVC detects a mapping between view name and the actual views, it will perform a forward to the view.
  881. As we saw on the <<switch-filter-all-dispatcher-types,previous section>>, Spring Security 6.0 will apply authorization to `FORWARD` requests by default.
  882. Consider the following common configuration:
  883. ====
  884. .Java
  885. [source,java,role="primary"]
  886. ----
  887. @Bean
  888. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  889. http
  890. .authorizeHttpRequests((authorize) -> authorize
  891. .shouldFilterAllDispatcherTypes(true)
  892. .requestMatchers("/").authenticated()
  893. .anyRequest().denyAll()
  894. )
  895. .formLogin((form) -> form
  896. .loginPage("/login")
  897. .permitAll()
  898. ));
  899. return http.build();
  900. }
  901. ----
  902. ====
  903. and one of the following equivalents MVC view mapping configurations:
  904. ====
  905. .Java
  906. [source,java,role="primary"]
  907. ----
  908. @Controller
  909. public class MyController {
  910. @GetMapping("/login")
  911. public String login() {
  912. return "login";
  913. }
  914. }
  915. ----
  916. ====
  917. ====
  918. .Java
  919. [source,java,role="primary"]
  920. ----
  921. @Configuration
  922. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  923. @Override
  924. public void addViewControllers(ViewControllerRegistry registry) {
  925. registry.addViewController("/login").setViewName("login");
  926. }
  927. }
  928. ----
  929. ====
  930. 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.
  931. 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`.
  932. To fix this, you should configure Spring Security to permit `FORWARD` requests:
  933. ====
  934. .Java
  935. [source,java,role="primary"]
  936. ----
  937. http
  938. .authorizeHttpRequests((authorize) -> authorize
  939. .shouldFilterAllDispatcherTypes(true)
  940. .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
  941. .anyRequest().denyAll()
  942. )
  943. // ...
  944. ----
  945. .Kotlin
  946. [source,kotlin,role="secondary"]
  947. ----
  948. http {
  949. authorizeHttpRequests {
  950. shouldFilterAllDispatcherTypes = true
  951. authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
  952. authorize(anyRequest, denyAll)
  953. }
  954. }
  955. ----
  956. .Xml
  957. [source,xml,role="secondary"]
  958. ----
  959. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  960. <intercept-url request-matcher-ref="forwardRequestMatcher" access="permitAll()" />
  961. <!-- ... -->
  962. <intercept-url pattern="/**" access="denyAll"/>
  963. </http>
  964. <bean name="forwardRequestMatcher" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  965. <constructor-arg value="FORWARD"/>
  966. </bean>
  967. ----
  968. ====
  969. === Replace any custom filter-security ``AccessDecisionManager``s
  970. 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.
  971. The preparation strategy will depend on your reason for each arrangement.
  972. Read on to find the best match for your situation.
  973. ==== I use `UnanimousBased`
  974. 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:
  975. ====
  976. .Java
  977. [source,java,role="primary"]
  978. ----
  979. @Bean
  980. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  981. PolicyAuthorizationManager policy = ...;
  982. LocalAuthorizationManager local = ...;
  983. return AuthorizationMangers.allOf(policy, local);
  984. }
  985. ----
  986. .Kotlin
  987. [source,kotlin,role="secondary"]
  988. ----
  989. @Bean
  990. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  991. val policy: PolicyAuthorizationManager = ...
  992. val local: LocalAuthorizationManager = ...
  993. return AuthorizationMangers.allOf(policy, local)
  994. }
  995. ----
  996. .Xml
  997. [source,xml,role="secondary"]
  998. ----
  999. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1000. factory-method="allOf">
  1001. <constructor-arg>
  1002. <util:list>
  1003. <bean class="my.PolicyAuthorizationManager"/>
  1004. <bean class="my.LocalAuthorizationManager"/>
  1005. </util:list>
  1006. </constructor-arg>
  1007. </bean>
  1008. ----
  1009. ====
  1010. then, wire it into the DSL like so:
  1011. ====
  1012. .Java
  1013. [source,java,role="primary"]
  1014. ----
  1015. http
  1016. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1017. // ...
  1018. ----
  1019. .Kotlin
  1020. [source,kotlin,role="secondary"]
  1021. ----
  1022. http {
  1023. authorizeHttpRequests {
  1024. authorize(anyRequest, requestAuthorization)
  1025. }
  1026. // ...
  1027. }
  1028. ----
  1029. .Xml
  1030. [source,xml,role="secondary"]
  1031. ----
  1032. <http authorization-manager-ref="requestAuthorization"/>
  1033. ----
  1034. ====
  1035. [NOTE]
  1036. ====
  1037. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1038. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1039. ====
  1040. ==== I use `AffirmativeBased`
  1041. 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:
  1042. ====
  1043. .Java
  1044. [source,java,role="primary"]
  1045. ----
  1046. @Bean
  1047. AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
  1048. PolicyAuthorizationManager policy = ...;
  1049. LocalAuthorizationManager local = ...;
  1050. return AuthorizationMangers.anyOf(policy, local);
  1051. }
  1052. ----
  1053. .Kotlin
  1054. [source,kotlin,role="secondary"]
  1055. ----
  1056. @Bean
  1057. fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
  1058. val policy: PolicyAuthorizationManager = ...
  1059. val local: LocalAuthorizationManager = ...
  1060. return AuthorizationMangers.anyOf(policy, local)
  1061. }
  1062. ----
  1063. .Xml
  1064. [source,xml,role="secondary"]
  1065. ----
  1066. <bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
  1067. factory-method="anyOf">
  1068. <constructor-arg>
  1069. <util:list>
  1070. <bean class="my.PolicyAuthorizationManager"/>
  1071. <bean class="my.LocalAuthorizationManager"/>
  1072. </util:list>
  1073. </constructor-arg>
  1074. </bean>
  1075. ----
  1076. ====
  1077. then, wire it into the DSL like so:
  1078. ====
  1079. .Java
  1080. [source,java,role="primary"]
  1081. ----
  1082. http
  1083. .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
  1084. // ...
  1085. ----
  1086. .Kotlin
  1087. [source,kotlin,role="secondary"]
  1088. ----
  1089. http {
  1090. authorizeHttpRequests {
  1091. authorize(anyRequest, requestAuthorization)
  1092. }
  1093. // ...
  1094. }
  1095. ----
  1096. .Xml
  1097. [source,xml,role="secondary"]
  1098. ----
  1099. <http authorization-manager-ref="requestAuthorization"/>
  1100. ----
  1101. ====
  1102. [NOTE]
  1103. ====
  1104. `authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
  1105. See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
  1106. ====
  1107. ==== I use `ConsensusBased`
  1108. There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
  1109. 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.
  1110. 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`].
  1111. ==== I use a custom `AccessDecisionVoter`
  1112. You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
  1113. Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
  1114. 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:
  1115. ====
  1116. .Java
  1117. [source,java,role="primary"]
  1118. ----
  1119. public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
  1120. private final SecurityMetadataSource metadata;
  1121. private final AccessDecisionVoter voter;
  1122. public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
  1123. Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
  1124. AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
  1125. this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
  1126. WebExpressionVoter voter = new WebExpressionVoter();
  1127. voter.setExpressionHandler(expressionHandler);
  1128. this.voter = voter;
  1129. }
  1130. public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
  1131. List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
  1132. int decision = this.voter.vote(authentication.get(), invocation, attributes);
  1133. if (decision == ACCESS_GRANTED) {
  1134. return new AuthorizationDecision(true);
  1135. }
  1136. if (decision == ACCESS_DENIED) {
  1137. return new AuthorizationDecision(false);
  1138. }
  1139. return null; // abstain
  1140. }
  1141. }
  1142. ----
  1143. ====
  1144. 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`].
  1145. [[servlet-authorizationmanager-requests-opt-out]]
  1146. === Opt-out Steps
  1147. In case you had trouble, take a look at these scenarios for optimal opt out behavior:
  1148. ==== I cannot secure all dispatcher types
  1149. If you cannot secure all dispatcher types, first try and declare which dispatcher types should not require authorization like so:
  1150. ====
  1151. .Java
  1152. [source,java,role="primary"]
  1153. ----
  1154. http
  1155. .authorizeHttpRequests((authorize) -> authorize
  1156. .shouldFilterAllDispatcherTypes(true)
  1157. .dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
  1158. .mvcMatchers("/app/**").hasRole("APP")
  1159. // ...
  1160. .anyRequest().denyAll()
  1161. )
  1162. // ...
  1163. ----
  1164. .Kotlin
  1165. [source,kotlin,role="secondary"]
  1166. ----
  1167. http {
  1168. authorizeHttpRequests {
  1169. shouldFilterAllDispatcherTypes = true
  1170. authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
  1171. authorize("/app/**", hasRole("APP"))
  1172. // ...
  1173. authorize(anyRequest, denyAll)
  1174. }
  1175. }
  1176. ----
  1177. .Xml
  1178. [source,xml,role="secondary"]
  1179. ----
  1180. <http filter-all-dispatcher-types="true" use-authorization-manager="true">
  1181. <intercept-url request-matcher-ref="dispatchers"/>
  1182. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1183. <!-- ... -->
  1184. <intercept-url pattern="/**" access="denyAll"/>
  1185. </http>
  1186. <bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
  1187. <constructor-arg>
  1188. <util:list value-type="javax.servlet.DispatcherType">
  1189. <value>FORWARD</value>
  1190. <value>INCLUDE</value>
  1191. </util:list>
  1192. </constructor-arg>
  1193. </bean>
  1194. ----
  1195. ====
  1196. 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`:
  1197. ====
  1198. .Java
  1199. [source,java,role="primary"]
  1200. ----
  1201. http
  1202. .authorizeHttpRequests((authorize) -> authorize
  1203. .filterAllDispatcherTypes(false)
  1204. .mvcMatchers("/app/**").hasRole("APP")
  1205. // ...
  1206. )
  1207. // ...
  1208. ----
  1209. .Kotlin
  1210. [source,kotlin,role="secondary"]
  1211. ----
  1212. http {
  1213. authorizeHttpRequests {
  1214. filterAllDispatcherTypes = false
  1215. authorize("/messages/**", hasRole("APP"))
  1216. // ...
  1217. }
  1218. }
  1219. ----
  1220. .Xml
  1221. [source,xml,role="secondary"]
  1222. ----
  1223. <http filter-all-dispatcher-types="false" use-authorization-manager="true">
  1224. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1225. <!-- ... -->
  1226. </http>
  1227. ----
  1228. ====
  1229. or, if you are still using `authorizeRequests` or `use-authorization-manager="false"`, set `oncePerRequest` to `true`:
  1230. ====
  1231. .Java
  1232. [source,java,role="primary"]
  1233. ----
  1234. http
  1235. .authorizeRequests((authorize) -> authorize
  1236. .filterSecurityInterceptorOncePerRequest(true)
  1237. .mvcMatchers("/app/**").hasRole("APP")
  1238. // ...
  1239. )
  1240. // ...
  1241. ----
  1242. .Kotlin
  1243. [source,kotlin,role="secondary"]
  1244. ----
  1245. http {
  1246. authorizeRequests {
  1247. filterSecurityInterceptorOncePerRequest = true
  1248. authorize("/messages/**", hasRole("APP"))
  1249. // ...
  1250. }
  1251. }
  1252. ----
  1253. .Xml
  1254. [source,xml,role="secondary"]
  1255. ----
  1256. <http once-per-request="true" use-authorization-manager="false">
  1257. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1258. <!-- ... -->
  1259. </http>
  1260. ----
  1261. ====
  1262. ==== I cannot declare an authorization rule for all requests
  1263. 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:
  1264. ====
  1265. .Java
  1266. [source,java,role="primary"]
  1267. ----
  1268. http
  1269. .authorizeHttpReqeusts((authorize) -> authorize
  1270. .mvcMatchers("/app/*").hasRole("APP")
  1271. // ...
  1272. .anyRequest().permitAll()
  1273. )
  1274. ----
  1275. .Kotlin
  1276. [source,kotlin,role="secondary"]
  1277. ----
  1278. http {
  1279. authorizeHttpRequests {
  1280. authorize("/app*", hasRole("APP"))
  1281. // ...
  1282. authorize(anyRequest, permitAll)
  1283. }
  1284. }
  1285. ----
  1286. .Xml
  1287. [source,xml,role="secondary"]
  1288. ----
  1289. <http>
  1290. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1291. <!-- ... -->
  1292. <intercept-url pattern="/**" access="permitAll"/>
  1293. </http>
  1294. ----
  1295. ====
  1296. ==== I cannot migrate my SpEL or my `AccessDecisionManager`
  1297. 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.
  1298. 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.
  1299. Second, if you still need your custom `access-decision-manager-ref` or have some other reason to opt out of `AuthorizationManager`, do:
  1300. ====
  1301. .Xml
  1302. [source,xml,role="secondary"]
  1303. ----
  1304. <http use-authorization-manager="false">
  1305. <intercept-url pattern="/app/*" access="hasRole('APP')"/>
  1306. <!-- ... -->
  1307. </http>
  1308. ----
  1309. ====