authorize-http-requests.adoc 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. [[servlet-authorization-authorizationfilter]]
  2. = Authorize HttpServletRequests
  3. :figures: servlet/authorization
  4. Spring Security allows you to xref:servlet/authorization/index.adoc[model your authorization] at the request level.
  5. For example, with Spring Security you can say that all pages under `/admin` require one authority while all other pages simply require authentication.
  6. By default, Spring Security requires that every request be authenticated.
  7. That said, any time you use xref:servlet/configuration/java.adoc#jc-httpsecurity[an `HttpSecurity` instance], it's necessary to declare your authorization rules.
  8. [[activate-request-security]]
  9. Whenever you have an `HttpSecurity` instance, you should at least do:
  10. .Use authorizeHttpRequests
  11. [tabs]
  12. ======
  13. Java::
  14. +
  15. [source,java,role="primary"]
  16. ----
  17. http
  18. .authorizeHttpRequests((authorize) -> authorize
  19. .anyRequest().authenticated()
  20. )
  21. ----
  22. Kotlin::
  23. +
  24. [source,kotlin,role="secondary"]
  25. ----
  26. http {
  27. authorizeHttpRequests {
  28. authorize(anyRequest, authenticated)
  29. }
  30. }
  31. ----
  32. Xml::
  33. +
  34. [source,xml,role="secondary"]
  35. ----
  36. <http>
  37. <intercept-url pattern="/**" access="authenticated"/>
  38. </http>
  39. ----
  40. ======
  41. This tells Spring Security that any endpoint in your application requires that the security context at a minimum be authenticated in order to allow it.
  42. In many cases, your authorization rules will be more sophisticated than that, so please consider the following use cases:
  43. * I have an app that uses `authorizeRequests` and I want to <<migrate-authorize-requests,migrate it to `authorizeHttpRequests`>>
  44. * I want to <<request-authorization-architecture,understand how the `AuthorizationFilter` components work>>
  45. * I want to <<match-requests, match requests>> based on a pattern; specifically <<match-by-regex,regex>>
  46. * I want to match request, and I map Spring MVC to <<mvc-not-default-servlet, something other than the default servlet>>
  47. * I want to <<authorize-requests, authorize requests>>
  48. * I want to <<match-by-custom, match a request programmatically>>
  49. * I want to <<authorize-requests, authorize a request programmatically>>
  50. * I want to <<remote-authorization-manager, delegate request authorization>> to a policy agent
  51. [[request-authorization-architecture]]
  52. == Understanding How Request Authorization Components Work
  53. [NOTE]
  54. This section builds on xref:servlet/architecture.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works at the request level in Servlet-based applications.
  55. .Authorize HttpServletRequest
  56. image::{figures}/authorizationfilter.png[]
  57. * image:{icondir}/number_1.png[] First, the `AuthorizationFilter` constructs a `Supplier` that retrieves an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
  58. * image:{icondir}/number_2.png[] Second, it passes the `Supplier<Authentication>` and the `HttpServletRequest` to the xref:servlet/architecture.adoc#authz-authorization-manager[`AuthorizationManager`].
  59. The `AuthorizationManager` matches the request to the patterns in `authorizeHttpRequests`, and runs the corresponding rule.
  60. ** image:{icondir}/number_3.png[] If authorization is denied, xref:servlet/authorization/events.adoc[an `AuthorizationDeniedEvent` is published], and an `AccessDeniedException` is thrown.
  61. In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`.
  62. ** image:{icondir}/number_4.png[] If access is granted, xref:servlet/authorization/events.adoc[an `AuthorizationGrantedEvent` is published] and `AuthorizationFilter` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally.
  63. === `AuthorizationFilter` Is Last By Default
  64. The `AuthorizationFilter` is last in xref:servlet/architecture.adoc#servlet-filterchain-figure[the Spring Security filter chain] by default.
  65. This means that Spring Security's xref:servlet/authentication/index.adoc[authentication filters], xref:servlet/exploits/index.adoc[exploit protections], and other filter integrations do not require authorization.
  66. If you add filters of your own before the `AuthorizationFilter`, they will also not require authorization; otherwise, they will.
  67. A place where this typically becomes important is when you are adding {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoints.
  68. Because they are executed by the {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`] and this comes after the `AuthorizationFilter`, your endpoints need to be <<authorizing-endpoints,included in `authorizeHttpRequests` to be permitted>>.
  69. === All Dispatches Are Authorized
  70. The `AuthorizationFilter` runs not just on every request, but on every dispatch.
  71. This means that the `REQUEST` dispatch needs authorization, but also ``FORWARD``s, ``ERROR``s, and ``INCLUDE``s.
  72. For example, {spring-framework-reference-url}web.html#spring-web[Spring MVC] can `FORWARD` the request to a view resolver that renders a Thymeleaf template, like so:
  73. .Sample Forwarding Spring MVC Controller
  74. [tabs]
  75. ======
  76. Java::
  77. +
  78. [source,java,role="primary"]
  79. ----
  80. @Controller
  81. public class MyController {
  82. @GetMapping("/endpoint")
  83. public String endpoint() {
  84. return "endpoint";
  85. }
  86. }
  87. ----
  88. Kotlin::
  89. +
  90. [source,kotlin,role="secondary"]
  91. ----
  92. @Controller
  93. class MyController {
  94. @GetMapping("/endpoint")
  95. fun endpoint(): String {
  96. return "endpoint"
  97. }
  98. }
  99. ----
  100. ======
  101. In this case, authorization happens twice; once for authorizing `/endpoint` and once for forwarding to Thymeleaf to render the "endpoint" template.
  102. For that reason, you may want to <<match-by-dispatcher-type, permit all `FORWARD` dispatches>>.
  103. Another example of this principle is {spring-boot-reference-url}web.html#web.servlet.spring-mvc.error-handling[how Spring Boot handles errors].
  104. If the container catches an exception, say like the following:
  105. .Sample Erroring Spring MVC Controller
  106. [tabs]
  107. ======
  108. Java::
  109. +
  110. [source,java,role="primary"]
  111. ----
  112. @Controller
  113. public class MyController {
  114. @GetMapping("/endpoint")
  115. public String endpoint() {
  116. throw new UnsupportedOperationException("unsupported");
  117. }
  118. }
  119. ----
  120. Kotlin::
  121. +
  122. [source,kotlin,role="secondary"]
  123. ----
  124. @Controller
  125. class MyController {
  126. @GetMapping("/endpoint")
  127. fun endpoint(): String {
  128. throw UnsupportedOperationException("unsupported")
  129. }
  130. }
  131. ----
  132. ======
  133. then Boot will dispatch it to the `ERROR` dispatch.
  134. In that case, authorization also happens twice; once for authorizing `/endpoint` and once for dispatching the error.
  135. For that reason, you may want to <<match-by-dispatcher-type, permit all `ERROR` dispatches>>.
  136. === `Authentication` Lookup is Deferred
  137. Remember that xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[the `AuthorizationManager` API uses a `Supplier<Authentication>`].
  138. This matters with `authorizeHttpRequests` when requests are <<authorize-requests,always permitted or always denied>>.
  139. In those cases, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is not queried, making for a faster request.
  140. [[authorizing-endpoints]]
  141. == Authorizing an Endpoint
  142. You can configure Spring Security to have different rules by adding more rules in order of precedence.
  143. If you want to require that `/endpoint` only be accessible by end users with the `USER` authority, then you can do:
  144. .Authorize an Endpoint
  145. [tabs]
  146. ======
  147. Java::
  148. +
  149. [source,java,role="primary"]
  150. ----
  151. @Bean
  152. SecurityFilterChain web(HttpSecurity http) throws Exception {
  153. http
  154. .authorizeHttpRequests((authorize) -> authorize
  155. .requestMatchers("/endpoint").hasAuthority('USER')
  156. .anyRequest().authenticated()
  157. )
  158. // ...
  159. return http.build();
  160. }
  161. ----
  162. Kotlin::
  163. +
  164. [source,kotlin,role="secondary"]
  165. ----
  166. @Bean
  167. SecurityFilterChain web(HttpSecurity http) throws Exception {
  168. http {
  169. authorizeHttpRequests {
  170. authorize("/endpoint", hasAuthority('USER'))
  171. authorize(anyRequest, authenticated)
  172. }
  173. }
  174. return http.build();
  175. }
  176. ----
  177. Xml::
  178. +
  179. [source,xml,role="secondary"]
  180. ----
  181. <http>
  182. <intercept-url pattern="/endpoint" access="hasAuthority('USER')"/>
  183. <intercept-url pattern="/**" access="authenticated"/>
  184. </http>
  185. ----
  186. ======
  187. As you can see, the declaration can be broken up in to pattern/rule pairs.
  188. `AuthorizationFilter` processes these pairs in the order listed, applying only the first match to the request.
  189. This means that even though `/**` would also match for `/endpoint` the above rules are not a problem.
  190. The way to read the above rules is "if the request is `/endpoint`, then require the `USER` authority; else, only require authentication".
  191. Spring Security supports several patterns and several rules; you can also programmatically create your own of each.
  192. Once authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:
  193. .Test Endpoint Authorization
  194. [tabs]
  195. ======
  196. Java::
  197. +
  198. [source,java,role="primary"]
  199. ----
  200. @WithMockUser(authorities="USER")
  201. @Test
  202. void endpointWhenUserAuthorityThenAuthorized() {
  203. this.mvc.perform(get("/endpoint"))
  204. .andExpect(status().isOk());
  205. }
  206. @WithMockUser
  207. @Test
  208. void endpointWhenNotUserAuthorityThenForbidden() {
  209. this.mvc.perform(get("/endpoint"))
  210. .andExpect(status().isForbidden());
  211. }
  212. @Test
  213. void anyWhenUnauthenticatedThenUnauthorized() {
  214. this.mvc.perform(get("/any"))
  215. .andExpect(status().isUnauthorized())
  216. }
  217. ----
  218. ======
  219. [[match-requests]]
  220. == Matching Requests
  221. Above you've already seen <<authorizing-endpoints, two ways to match requests>>.
  222. The first you saw was the simplest, which is to match any request.
  223. The second is to match by a URI pattern.
  224. Spring Security supports two languages for URI pattern-matching: <<match-by-ant,Ant>> (as seen above) and <<match-by-regex,Regular Expressions>>.
  225. [[match-by-ant]]
  226. === Matching Using Ant
  227. Ant is the default language that Spring Security uses to match requests.
  228. You can use it to match a single endpoint or a directory, and you can even capture placeholders for later use.
  229. You can also refine it to match a specific set of HTTP methods.
  230. Let's say that you instead of wanting to match the `/endpoint` endpoint, you want to match all endpoints under the `/resource` directory.
  231. In that case, you can do something like the following:
  232. .Match with Ant
  233. [tabs]
  234. ======
  235. Java::
  236. +
  237. [source,java,role="primary"]
  238. ----
  239. http
  240. .authorizeHttpRequests((authorize) -> authorize
  241. .requestMatchers("/resource/**").hasAuthority("USER")
  242. .anyRequest().authenticated()
  243. )
  244. ----
  245. Kotlin::
  246. +
  247. [source,kotlin,role="secondary"]
  248. ----
  249. http {
  250. authorizeHttpRequests {
  251. authorize("/resource/**", hasAuthority("USER"))
  252. authorize(anyRequest, authenticated)
  253. }
  254. }
  255. ----
  256. Xml::
  257. +
  258. [source,xml,role="secondary"]
  259. ----
  260. <http>
  261. <intercept-url pattern="/resource/**" access="hasAuthority('USER')"/>
  262. <intercept-url pattern="/**" access="authenticated"/>
  263. </http>
  264. ----
  265. ======
  266. The way to read this is "if the request is `/resource` or some subdirectory, require the `USER` authority; otherwise, only require authentication"
  267. You can also extract path values from the request, as seen below:
  268. .Authorize and Extract
  269. [tabs]
  270. ======
  271. Java::
  272. +
  273. [source,java,role="primary"]
  274. ----
  275. http
  276. .authorizeHttpRequests((authorize) -> authorize
  277. .requestMatchers("/resource/{name}").access(new WebExpressionAuthorizationManager("#name == authentication.name"))
  278. .anyRequest().authenticated()
  279. )
  280. ----
  281. Kotlin::
  282. +
  283. [source,kotlin,role="secondary"]
  284. ----
  285. http {
  286. authorizeHttpRequests {
  287. authorize("/resource/{name}", WebExpressionAuthorizationManager("#name == authentication.name"))
  288. authorize(anyRequest, authenticated)
  289. }
  290. }
  291. ----
  292. Xml::
  293. +
  294. [source,xml,role="secondary"]
  295. ----
  296. <http>
  297. <intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
  298. <intercept-url pattern="/**" access="authenticated"/>
  299. </http>
  300. ----
  301. ======
  302. Once authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:
  303. .Test Directory Authorization
  304. [tabs]
  305. ======
  306. Java::
  307. +
  308. [source,java,role="primary"]
  309. ----
  310. @WithMockUser(authorities="USER")
  311. @Test
  312. void endpointWhenUserAuthorityThenAuthorized() {
  313. this.mvc.perform(get("/endpoint/jon"))
  314. .andExpect(status().isOk());
  315. }
  316. @WithMockUser
  317. @Test
  318. void endpointWhenNotUserAuthorityThenForbidden() {
  319. this.mvc.perform(get("/endpoint/jon"))
  320. .andExpect(status().isForbidden());
  321. }
  322. @Test
  323. void anyWhenUnauthenticatedThenUnauthorized() {
  324. this.mvc.perform(get("/any"))
  325. .andExpect(status().isUnauthorized())
  326. }
  327. ----
  328. ======
  329. [NOTE]
  330. Spring Security only matches paths.
  331. If you want to match query parameters, you will need a custom request matcher.
  332. [[match-by-regex]]
  333. === Matching Using Regular Expressions
  334. Spring Security supports matching requests against a regular expression.
  335. This can come in handy if you want to apply more strict matching criteria than `**` on a subdirectory.
  336. For example, consider a path that contains the username and the rule that all usernames must be alphanumeric.
  337. You can use {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to respect this rule, like so:
  338. .Match with Regex
  339. [tabs]
  340. ======
  341. Java::
  342. +
  343. [source,java,role="primary"]
  344. ----
  345. http
  346. .authorizeHttpRequests((authorize) -> authorize
  347. .requestMatchers(RegexRequestMatcher.regexMatcher("/resource/[A-Za-z0-9]+")).hasAuthority("USER")
  348. .anyRequest().denyAll()
  349. )
  350. ----
  351. Kotlin::
  352. +
  353. [source,kotlin,role="secondary"]
  354. ----
  355. http {
  356. authorizeHttpRequests {
  357. authorize(RegexRequestMatcher.regexMatcher("/resource/[A-Za-z0-9]+"), hasAuthority("USER"))
  358. authorize(anyRequest, denyAll)
  359. }
  360. }
  361. ----
  362. Xml::
  363. +
  364. [source,xml,role="secondary"]
  365. ----
  366. <http>
  367. <intercept-url request-matcher="regex" pattern="/resource/[A-Za-z0-9]+" access="hasAuthority('USER')"/>
  368. <intercept-url pattern="/**" access="denyAll"/>
  369. </http>
  370. ----
  371. ======
  372. [[match-by-httpmethod]]
  373. === Matching By Http Method
  374. You can also match rules by HTTP method.
  375. One place where this is handy is when authorizing by permissions granted, like being granted a `read` or `write` privilege.
  376. To require all ``GET``s to have the `read` permission and all ``POST``s to have the `write` permission, you can do something like this:
  377. .Match by HTTP Method
  378. [tabs]
  379. ======
  380. Java::
  381. +
  382. [source,java,role="primary"]
  383. ----
  384. http
  385. .authorizeHttpRequests((authorize) -> authorize
  386. .requestMatchers(HttpMethod.GET).hasAuthority("read")
  387. .requestMatchers(HttpMethod.POST).hasAuthority("write")
  388. .anyRequest().denyAll()
  389. )
  390. ----
  391. Kotlin::
  392. +
  393. [source,kotlin,role="secondary"]
  394. ----
  395. http {
  396. authorizeHttpRequests {
  397. authorize(HttpMethod.GET, hasAuthority("read"))
  398. authorize(HttpMethod.POST, hasAuthority("write"))
  399. authorize(anyRequest, denyAll)
  400. }
  401. }
  402. ----
  403. Xml::
  404. +
  405. [source,xml,role="secondary"]
  406. ----
  407. <http>
  408. <intercept-url http-method="GET" pattern="/**" access="hasAuthority('read')"/>
  409. <intercept-url http-method="POST" pattern="/**" access="hasAuthority('write')"/>
  410. <intercept-url pattern="/**" access="denyAll"/>
  411. </http>
  412. ----
  413. ======
  414. These authorization rules should read as: "if the request is a GET, then require `read` permission; else, if the request is a POST, then require `write` permission; else, deny the request"
  415. [TIP]
  416. Denying the request by default is a healthy security practice since it turns the set of rules into an allow list.
  417. Once authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:
  418. .Test Http Method Authorization
  419. [tabs]
  420. ======
  421. Java::
  422. +
  423. [source,java,role="primary"]
  424. ----
  425. @WithMockUser(authorities="read")
  426. @Test
  427. void getWhenReadAuthorityThenAuthorized() {
  428. this.mvc.perform(get("/any"))
  429. .andExpect(status().isOk());
  430. }
  431. @WithMockUser
  432. @Test
  433. void getWhenNoReadAuthorityThenForbidden() {
  434. this.mvc.perform(get("/any"))
  435. .andExpect(status().isForbidden());
  436. }
  437. @WithMockUser(authorities="write")
  438. @Test
  439. void postWhenWriteAuthorityThenAuthorized() {
  440. this.mvc.perform(post("/any").with(csrf()))
  441. .andExpect(status().isOk())
  442. }
  443. @WithMockUser(authorities="read")
  444. @Test
  445. void postWhenNoWriteAuthorityThenForbidden() {
  446. this.mvc.perform(get("/any").with(csrf()))
  447. .andExpect(status().isForbidden());
  448. }
  449. ----
  450. ======
  451. [[match-by-dispatcher-type]]
  452. === Matching By Dispatcher Type
  453. [NOTE]
  454. This feature is not currently supported in XML
  455. As stated earlier, Spring Security <<_all_dispatches_are_authorized, authorizes all dispatcher types by default>>.
  456. And even though xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the security context] established on the `REQUEST` dispatch carries over to subsequent dispatches, subtle mismatches can sometimes cause an unexpected `AccessDeniedException`.
  457. To address that, you can configure Spring Security Java configuration to allow dispatcher types like `FORWARD` and `ERROR`, like so:
  458. .Match by Dispatcher Type
  459. ====
  460. .Java
  461. [source,java,role="secondary"]
  462. ----
  463. http
  464. .authorizeHttpRequests((authorize) -> authorize
  465. .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
  466. .requestMatchers("/endpoint").permitAll()
  467. .anyRequest().denyAll()
  468. )
  469. ----
  470. .Kotlin
  471. [source,kotlin,role="secondary"]
  472. ----
  473. http {
  474. authorizeHttpRequests {
  475. authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)
  476. authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), permitAll)
  477. authorize("/endpoint", permitAll)
  478. authorize(anyRequest, denyAll)
  479. }
  480. }
  481. ----
  482. ====
  483. [[match-by-servlet-path]]
  484. [[mvc-not-default-servlet]]
  485. [[match-by-mvc]]
  486. === Matching by Servlet Pattern
  487. Generally speaking, you can use `requestMatchers(String...)` and `requestMatchers(HttpMethod, String...)` as demonstrated above.
  488. However, if you map Spring MVC to a different servlet path, then you need to account for that in your security configuration.
  489. For example, if Spring MVC is mapped to `/mvc` instead of `/` (the default), then you may have an endpoint like `/mvc/my/controller` that you want to authorize.
  490. If you have multiple servlets, and `DispatcherServlet` is mapped in this way, you'll see an error that's something like this:
  491. [source,bash]
  492. ----
  493. This method cannot decide whether these patterns are Spring MVC patterns or not
  494. ...
  495. For your reference, here is your servlet configuration: {default=[/], dispatcherServlet=[/mvc/*]}
  496. To address this, you need to specify the servlet path or pattern for each endpoint.
  497. You can use .forServletPattern in conjunction with requestMatchers do to this
  498. ----
  499. You can use `.forServletPattern` (or construct your own `MvcRequestMatcher` instance) to split the servlet path and the controller path in your configuration, like so:
  500. .Match by MvcRequestMatcher
  501. ====
  502. .Java
  503. [source,java,role="primary"]
  504. ----
  505. @Bean
  506. SecurityFilterChain appEndpoints(HttpSecurity http, MvcRequestMatcher.Builder mvc) {
  507. http
  508. .authorizeHttpRequests((authorize) -> authorize
  509. .forServletPattern("/mvc/*", (mvc) -> mvc
  510. .requestMatchers("/my/resource/**").hasAuthority("resource:read")
  511. .anyRequest().authenticated()
  512. )
  513. );
  514. return http.build();
  515. }
  516. ----
  517. ====
  518. where `/mvc/*` is the matching pattern in your servlet configuration listed in the error message.
  519. This need can arise in at least two different ways:
  520. * If you use the `spring.mvc.servlet.path` Boot property to change the default path (`/`) to something else
  521. * If you register more than one Spring MVC `DispatcherServlet` (thus requiring that one of them not be the default servlet)
  522. Note that when either of these cases come up, all URIs need to be fully-qualified as above.
  523. For example, consider a more sophisticated setup where you have Spring MVC resources mapped to `/mvc/*` and Spring Boot H2 Console mapped to `/h2-console/*`.
  524. In that case, each URI can be made absolute, listing the servlet path like so:
  525. .Match by Servlet Path
  526. ====
  527. .Java
  528. [source,java,role="primary"]
  529. ----
  530. @Bean
  531. SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
  532. http
  533. .authorizeHttpRequests((authorize) -> authorize
  534. .forServletPattern("/mvc/*", (mvc) -> mvc
  535. .requestMatchers("/my/resource/**").hasAuthority("resource:read")
  536. )
  537. .forServletPattern("/h2-console/*", (h2) -> h2
  538. .anyRequest().hasAuthority("h2")
  539. )
  540. )
  541. // ...
  542. }
  543. ----
  544. ====
  545. Alternatively, you can do one of three things to remove the need to disambiguate:
  546. 1. Always deploy `DispatcherServlet` to `/` (the default behavior)
  547. +
  548. When `DispatcherServlet` is mapped to `/`, it's clear that all the URIs supplied in `requestMatchers(String)` are absolute URIs.
  549. Because of that, there is no ambiguity when interpreting them.
  550. +
  551. 2. Remove all other servlets
  552. +
  553. When there is only `DispatcherServlet`, it's clear that all the URIs supplied in `requestMatchers(String)` are relative to the Spring MVC configuration.
  554. Because of that, there is no ambiguity when interpreting them.
  555. At times, servlet containers add other servlets by default that you aren't actually using.
  556. So, if these aren't needed, remove them, bringing you down to just `DispatcherServlet`.
  557. +
  558. 3. Create an `HttpRequestHandler` so that `DispatcherServlet` dispatches to your servlets instead of your servlet container.
  559. +
  560. If you are deploying Spring MVC to a separate path to allow your container to serve static resources, consider instead {spring-framework-reference-url}web/webmvc/mvc-config/default-servlet-handler.html#page-title[notifying Spring MVC about this].
  561. Or, if you have a custom servlet, publishing {spring-framework-api-url}org/springframework/web/servlet/mvc/HttpRequestHandlerAdapter.html[a custom `HttpRequestHandler` bean within {spring-framework-api-url}org/springframework/web/servlet/DispatcherServlet.html[the `DispatcherServlet` configuration] instead.
  562. +
  563. === Matching by the Default Servlet
  564. You can also match more generally by the matching pattern specified in your servlet configuration.
  565. For example, to match the default servlet (whichever servlet is mapped to `/`), use `forServletPattern` like so:
  566. .Match by the Default Servlet
  567. ====
  568. .Java
  569. [source,java,role="primary"]
  570. ----
  571. @Bean
  572. SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
  573. http
  574. .authorizeHttpRequests((authorize) -> authorize
  575. .forServletPattern("/", (root) -> root
  576. .requestMatchers("/my/resource/**").hasAuthority("resource:read")
  577. )
  578. )
  579. // ...
  580. }
  581. ----
  582. ====
  583. Such will match on requests that the servlet container matches to your default servlet that start with the URI `/my/resource`.
  584. === Matching by an Extension Servlet
  585. Or, to match to an extension servlet (like a servlet mapped to `*.jsp`), use `forServletPattern` as follows:
  586. .Match by an Extension Servlet
  587. ====
  588. .Java
  589. [source,java,role="primary"]
  590. ----
  591. @Bean
  592. SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
  593. http
  594. .authorizeHttpRequests((authorize) -> authorize
  595. .forServletPattern("*.jsp", (jsp) -> jsp
  596. .requestMatchers("/my/resource/**").hasAuthority("resource:read")
  597. )
  598. )
  599. // ...
  600. }
  601. ----
  602. ====
  603. Such will match on requests that the servlet container matches to your `*.jsp` servlet that start with the URI `/my/resource` (for example a request like `/my/resource/page.jsp`).
  604. [[match-by-custom]]
  605. === Using a Custom Matcher
  606. [NOTE]
  607. This feature is not currently supported in XML
  608. In Java configuration, you can create your own {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] and supply it to the DSL like so:
  609. .Authorize by Dispatcher Type
  610. ====
  611. .Java
  612. [source,java,role="secondary"]
  613. ----
  614. RequestMatcher printview = (request) -> request.getParameter("print") != null;
  615. http
  616. .authorizeHttpRequests((authorize) -> authorize
  617. .requestMatchers(printview).hasAuthority("print")
  618. .anyRequest().authenticated()
  619. )
  620. ----
  621. .Kotlin
  622. [source,kotlin,role="secondary"]
  623. ----
  624. val printview: RequestMatcher = { (request) -> request.getParameter("print") != null }
  625. http {
  626. authorizeHttpRequests {
  627. authorize(printview, hasAuthority("print"))
  628. authorize(anyRequest, authenticated)
  629. }
  630. }
  631. ----
  632. ====
  633. [TIP]
  634. Because {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] is a functional interface, you can supply it as a lambda in the DSL.
  635. However, if you want to extract values from the request, you will need to have a concrete class since that requires overriding a `default` method.
  636. Once authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:
  637. .Test Custom Authorization
  638. [tabs]
  639. ======
  640. Java::
  641. +
  642. [source,java,role="primary"]
  643. ----
  644. @WithMockUser(authorities="print")
  645. @Test
  646. void printWhenPrintAuthorityThenAuthorized() {
  647. this.mvc.perform(get("/any?print"))
  648. .andExpect(status().isOk());
  649. }
  650. @WithMockUser
  651. @Test
  652. void printWhenNoPrintAuthorityThenForbidden() {
  653. this.mvc.perform(get("/any?print"))
  654. .andExpect(status().isForbidden());
  655. }
  656. ----
  657. ======
  658. [[authorize-requests]]
  659. == Authorizing Requests
  660. Once a request is matched, you can authorize it in several ways <<match-requests, already seen>> like `permitAll`, `denyAll`, and `hasAuthority`.
  661. As a quick summary, here are the authorization rules built into the DSL:
  662. * `permitAll` - The request requires no authorization and is a public endpoint; note that in this case, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is never retrieved from the session
  663. * `denyAll` - The request is not allowed under any circumstances; note that in this case, the `Authentication` is never retrieved from the session
  664. * `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value
  665. * `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
  666. * `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values
  667. * `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
  668. * `access` - The request uses this custom `AuthorizationManager` to determine access
  669. Having now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:
  670. .Authorize Requests
  671. [tabs]
  672. ======
  673. Java::
  674. +
  675. [source,java,role="primary"]
  676. ----
  677. import static jakarta.servlet.DispatcherType.*;
  678. import static org.springframework.security.authorization.AuthorizationManagers.allOf;
  679. import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
  680. import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;
  681. @Bean
  682. SecurityFilterChain web(HttpSecurity http) throws Exception {
  683. http
  684. // ...
  685. .authorizeHttpRequests(authorize -> authorize // <1>
  686. .dispatcherTypeMatchers(FORWARD, ERROR).permitAll() // <2>
  687. .requestMatchers("/static/**", "/signup", "/about").permitAll() // <3>
  688. .requestMatchers("/admin/**").hasRole("ADMIN") // <4>
  689. .requestMatchers("/db/**").access(allOf(hasAuthority('db'), hasRole('ADMIN'))) // <5>
  690. .anyRequest().denyAll() // <6>
  691. );
  692. return http.build();
  693. }
  694. ----
  695. ======
  696. <1> There are multiple authorization rules specified.
  697. Each rule is considered in the order they were declared.
  698. <2> Dispatches `FORWARD` and `ERROR` are permitted to allow {spring-framework-reference-url}web.html#spring-web[Spring MVC] to render views and Spring Boot to render errors
  699. <3> We specified multiple URL patterns that any user can access.
  700. Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about".
  701. <4> Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN".
  702. You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix.
  703. <5> Any URL that starts with "/db/" requires the user to have both been granted the "db" permission as well as be a "ROLE_ADMIN".
  704. You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix.
  705. <6> Any URL that has not already been matched on is denied access.
  706. This is a good strategy if you do not want to accidentally forget to update your authorization rules.
  707. [[authorization-expressions]]
  708. == Expressing Authorization with SpEL
  709. While using a concrete `AuthorizationManager` is recommended, there are some cases where an expression is necessary, like with `<intercept-url>` or with JSP Taglibs.
  710. For that reason, this section will focus on examples from those domains.
  711. Given that, let's cover Spring Security's Web Security Authorization SpEL API a bit more in depth.
  712. Spring Security encapsulates all of its authorization fields and methods in a set of root objects.
  713. The most generic root object is called `SecurityExpressionRoot` and it forms the basis for `WebSecurityExpressionRoot`.
  714. Spring Security supplies this root object to `StandardEvaluationContext` when preparing to evaluate an authorization expression.
  715. [[using-authorization-expression-fields-and-methods]]
  716. === Using Authorization Expression Fields and Methods
  717. The first thing this provides is an enhanced set of authorization fields and methods to your SpEL expressions.
  718. What follows is a quick overview of the most common methods:
  719. * `permitAll` - The request requires no authorization to be invoked; note that in this case, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is never retrieved from the session
  720. * `denyAll` - The request is not allowed under any circumstances; note that in this case, the `Authentication` is never retrieved from the session
  721. * `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value
  722. * `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
  723. * `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values
  724. * `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
  725. * `hasPermission` - A hook into your `PermissionEvaluator` instance for doing object-level authorization
  726. And here is a brief look at the most common fields:
  727. * `authentication` - The `Authentication` instance associated with this method invocation
  728. * `principal` - The `Authentication#getPrincipal` associated with this method invocation
  729. Having now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:
  730. .Authorize Requests Using SpEL
  731. [tabs]
  732. ======
  733. Xml::
  734. +
  735. [source,java,role="primary"]
  736. ----
  737. <http>
  738. <intercept-url pattern="/static/**" access="permitAll"/> <1>
  739. <intercept-url pattern="/admin/**" access="hasRole('ADMIN')"/> <2>
  740. <intercept-url pattern="/db/**" access="hasAuthority('db') and hasRole('ADMIN')"/> <3>
  741. <intercept-url pattern="/**" access="denyAll"/> <4>
  742. </http>
  743. ----
  744. ======
  745. <1> We specified a URL patters that any user can access.
  746. Specifically, any user can access a request if the URL starts with "/static/".
  747. <2> Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN".
  748. You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix.
  749. <3> Any URL that starts with "/db/" requires the user to have both been granted the "db" permission as well as be a "ROLE_ADMIN".
  750. You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix.
  751. <4> Any URL that has not already been matched on is denied access.
  752. This is a good strategy if you do not want to accidentally forget to update your authorization rules.
  753. [[using_path_parameters]]
  754. === Using Path Parameters
  755. Additionally, Spring Security provides a mechanism for discovering path parameters so they can also be accessed in the SpEL expression as well.
  756. For example, you can access a path parameter in your SpEL expression in the following way:
  757. .Authorize Request using SpEL path variable
  758. [tabs]
  759. ======
  760. Xml::
  761. +
  762. [source,xml,role="primary"]
  763. ----
  764. <http>
  765. <intercept-url pattern="/resource/{name}" access="#name == authentication.name"/>
  766. <intercept-url pattern="/**" access="authenticated"/>
  767. </http>
  768. ----
  769. ======
  770. This expression refers to the path variable after `/resource/` and requires that it is equal to `Authentication#getName`.
  771. [[remote-authorization-manager]]
  772. === Use an Authorization Database, Policy Agent, or Other Service
  773. If you want to configure Spring Security to use a separate service for authorization, you can create your own `AuthorizationManager` and match it to `anyRequest`.
  774. First, your `AuthorizationManager` may look something like this:
  775. .Open Policy Agent Authorization Manager
  776. [tabs]
  777. ======
  778. Java::
  779. +
  780. [source,java,role="primary"]
  781. ----
  782. @Component
  783. public final class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
  784. @Override
  785. public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
  786. // make request to Open Policy Agent
  787. }
  788. }
  789. ----
  790. ======
  791. Then, you can wire it into Spring Security in the following way:
  792. .Any Request Goes to Remote Service
  793. [tabs]
  794. ======
  795. Java::
  796. +
  797. [source,java,role="primary"]
  798. ----
  799. @Bean
  800. SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {
  801. http
  802. // ...
  803. .authorizeHttpRequests((authorize) -> authorize
  804. .anyRequest().access(authz)
  805. );
  806. return http.build();
  807. }
  808. ----
  809. ======
  810. [[favor-permitall]]
  811. === Favor `permitAll` over `ignoring`
  812. When you have static resources it can be tempting to configure the filter chain to ignore these values.
  813. A more secure approach is to permit them using `permitAll` like so:
  814. .Permit Static Resources
  815. ====
  816. .Java
  817. [source,java,role="secondary"]
  818. ----
  819. http
  820. .authorizeHttpRequests((authorize) -> authorize
  821. .requestMatchers("/css/**").permitAll()
  822. .anyRequest().authenticated()
  823. )
  824. ----
  825. .Kotlin
  826. [source,kotlin,role="secondary"]
  827. ----
  828. http {
  829. authorizeHttpRequests {
  830. authorize("/css/**", permitAll)
  831. authorize(anyRequest, authenticated)
  832. }
  833. }
  834. ----
  835. ====
  836. It's more secure because even with static resources it's important to write secure headers, which Spring Security cannot do if the request is ignored.
  837. In this past, this came with a performance tradeoff since the session was consulted by Spring Security on every request.
  838. As of Spring Security 6, however, the session is no longer pinged unless required by the authorization rule.
  839. Because the performance impact is now addressed, Spring Security recommends using at least `permitAll` for all requests.
  840. [[migrate-authorize-requests]]
  841. == Migrating from `authorizeRequests`
  842. [NOTE]
  843. `AuthorizationFilter` supersedes {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`].
  844. To remain backward compatible, `FilterSecurityInterceptor` remains the default.
  845. This section discusses how `AuthorizationFilter` works and how to override the default configuration.
  846. The {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s.
  847. It is inserted into the xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture.adoc#servlet-security-filters[Security Filters].
  848. You can override the default when you declare a `SecurityFilterChain`.
  849. Instead of using {security-api-url}org/springframework/security/config/annotation/web/builders/HttpSecurity.html#authorizeRequests()[`authorizeRequests`], use `authorizeHttpRequests`, like so:
  850. .Use authorizeHttpRequests
  851. [tabs]
  852. ======
  853. Java::
  854. +
  855. [source,java,role="primary"]
  856. ----
  857. @Bean
  858. SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
  859. http
  860. .authorizeHttpRequests((authorize) -> authorize
  861. .anyRequest().authenticated();
  862. )
  863. // ...
  864. return http.build();
  865. }
  866. ----
  867. ======
  868. This improves on `authorizeRequests` in a number of ways:
  869. 1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.
  870. This simplifies reuse and customization.
  871. 2. Delays `Authentication` lookup.
  872. Instead of the authentication needing to be looked up for every request, it will only look it up in requests where an authorization decision requires authentication.
  873. 3. Bean-based configuration support.
  874. When `authorizeHttpRequests` is used instead of `authorizeRequests`, then {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] is used instead of {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`].
  875. === Migrating Expressions
  876. Where possible, it is recommended that you use type-safe authorization managers instead of SpEL.
  877. For Java configuration, {security-api-url}org/springframework/security/web/access/expression/WebExpressionAuthorizationManager.html[`WebExpressionAuthorizationManager`] is available to help migrate legacy SpEL.
  878. To use `WebExpressionAuthorizationManager`, you can construct one with the expression you are trying to migrate, like so:
  879. [tabs]
  880. ======
  881. Java::
  882. +
  883. [source,java,role="primary"]
  884. ----
  885. .requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
  886. ----
  887. Kotlin::
  888. +
  889. [source,kotlin,role="secondary"]
  890. ----
  891. .requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
  892. ----
  893. ======
  894. If you are referring to a bean in your expression like so: `@webSecurity.check(authentication, request)`, it's recommended that you instead call the bean directly, which will look something like the following:
  895. [tabs]
  896. ======
  897. Java::
  898. +
  899. [source,java,role="primary"]
  900. ----
  901. .requestMatchers("/test/**").access((authentication, context) ->
  902. new AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))
  903. ----
  904. Kotlin::
  905. +
  906. [source,kotlin,role="secondary"]
  907. ----
  908. .requestMatchers("/test/**").access((authentication, context): AuthorizationManager<RequestAuthorizationContext> ->
  909. AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))
  910. ----
  911. ======
  912. For complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`.
  913. If you are not able to do that, you can configure a {security-api-url}org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.html[`DefaultHttpSecurityExpressionHandler`] with a bean resolver and supply that to `WebExpressionAuthorizationManager#setExpressionhandler`.
  914. [[security-matchers]]
  915. == Security Matchers
  916. The {security-api-url}org/springframework/security/web/util/matcher/RequestMatcher.html[`RequestMatcher`] interface is used to determine if a request matches a given rule.
  917. We use `securityMatchers` to determine if xref:servlet/configuration/java.adoc#jc-httpsecurity[a given `HttpSecurity`] should be applied to a given request.
  918. The same way, we can use `requestMatchers` to determine the authorization rules that we should apply to a given request.
  919. Look at the following example:
  920. [tabs]
  921. ======
  922. Java::
  923. +
  924. [source,java,role="primary"]
  925. ----
  926. @Configuration
  927. @EnableWebSecurity
  928. public class SecurityConfig {
  929. @Bean
  930. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  931. http
  932. .securityMatcher("/api/**") <1>
  933. .authorizeHttpRequests(authorize -> authorize
  934. .requestMatchers("/user/**").hasRole("USER") <2>
  935. .requestMatchers("/admin/**").hasRole("ADMIN") <3>
  936. .anyRequest().authenticated() <4>
  937. )
  938. .formLogin(withDefaults());
  939. return http.build();
  940. }
  941. }
  942. ----
  943. Kotlin::
  944. +
  945. [source,kotlin,role="secondary"]
  946. ----
  947. @Configuration
  948. @EnableWebSecurity
  949. open class SecurityConfig {
  950. @Bean
  951. open fun web(http: HttpSecurity): SecurityFilterChain {
  952. http {
  953. securityMatcher("/api/**") <1>
  954. authorizeHttpRequests {
  955. authorize("/user/**", hasRole("USER")) <2>
  956. authorize("/admin/**", hasRole("ADMIN")) <3>
  957. authorize(anyRequest, authenticated) <4>
  958. }
  959. }
  960. return http.build()
  961. }
  962. }
  963. ----
  964. ======
  965. <1> Configure `HttpSecurity` to only be applied to URLs that start with `/api/`
  966. <2> Allow access to URLs that start with `/user/` to users with the `USER` role
  967. <3> Allow access to URLs that start with `/admin/` to users with the `ADMIN` role
  968. <4> Any other request that doesn't match the rules above, will require authentication
  969. The `securityMatcher(s)` and `requestMatcher(s)` methods will decide which `RequestMatcher` implementation fits best for your application: If {spring-framework-reference-url}web.html#spring-web[Spring MVC] is in the classpath, then {security-api-url}org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.html[`MvcRequestMatcher`] will be used, otherwise, {security-api-url}org/springframework/security/web/servlet/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] will be used.
  970. You can read more about the Spring MVC integration xref:servlet/integrations/mvc.adoc[here].
  971. If you want to use a specific `RequestMatcher`, just pass an implementation to the `securityMatcher` and/or `requestMatcher` methods:
  972. [tabs]
  973. ======
  974. Java::
  975. +
  976. [source,java,role="primary"]
  977. ----
  978. import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; <1>
  979. import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;
  980. @Configuration
  981. @EnableWebSecurity
  982. public class SecurityConfig {
  983. @Bean
  984. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  985. http
  986. .securityMatcher(antMatcher("/api/**")) <2>
  987. .authorizeHttpRequests(authorize -> authorize
  988. .requestMatchers(antMatcher("/user/**")).hasRole("USER") <3>
  989. .requestMatchers(regexMatcher("/admin/.*")).hasRole("ADMIN") <4>
  990. .requestMatchers(new MyCustomRequestMatcher()).hasRole("SUPERVISOR") <5>
  991. .anyRequest().authenticated()
  992. )
  993. .formLogin(withDefaults());
  994. return http.build();
  995. }
  996. }
  997. public class MyCustomRequestMatcher implements RequestMatcher {
  998. @Override
  999. public boolean matches(HttpServletRequest request) {
  1000. // ...
  1001. }
  1002. }
  1003. ----
  1004. Kotlin::
  1005. +
  1006. [source,kotlin,role="secondary"]
  1007. ----
  1008. import org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher <1>
  1009. import org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher
  1010. @Configuration
  1011. @EnableWebSecurity
  1012. open class SecurityConfig {
  1013. @Bean
  1014. open fun web(http: HttpSecurity): SecurityFilterChain {
  1015. http {
  1016. securityMatcher(antMatcher("/api/**")) <2>
  1017. authorizeHttpRequests {
  1018. authorize(antMatcher("/user/**"), hasRole("USER")) <3>
  1019. authorize(regexMatcher("/admin/**"), hasRole("ADMIN")) <4>
  1020. authorize(MyCustomRequestMatcher(), hasRole("SUPERVISOR")) <5>
  1021. authorize(anyRequest, authenticated)
  1022. }
  1023. }
  1024. return http.build()
  1025. }
  1026. }
  1027. ----
  1028. ======
  1029. <1> Import the static factory methods from `AntPathRequestMatcher` and `RegexRequestMatcher` to create `RequestMatcher` instances.
  1030. <2> Configure `HttpSecurity` to only be applied to URLs that start with `/api/`, using `AntPathRequestMatcher`
  1031. <3> Allow access to URLs that start with `/user/` to users with the `USER` role, using `AntPathRequestMatcher`
  1032. <4> Allow access to URLs that start with `/admin/` to users with the `ADMIN` role, using `RegexRequestMatcher`
  1033. <5> Allow access to URLs that match the `MyCustomRequestMatcher` to users with the `SUPERVISOR` role, using a custom `RequestMatcher`
  1034. == Further Reading
  1035. Now that you have secured your application's requests, consider xref:servlet/authorization/method-security.adoc[securing its methods].
  1036. You can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of you application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics].