2
0

java.adoc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. [[jc]]
  2. = Java Configuration
  3. General support for {spring-framework-reference-url}core/beans/java.html[Java configuration] was added to Spring Framework in Spring 3.1.
  4. Spring Security 3.2 introduced Java configuration to let users configure Spring Security without the use of any XML.
  5. If you are familiar with the xref:servlet/configuration/xml-namespace.adoc#ns-config[Security Namespace Configuration], you should find quite a few similarities between it and Spring Security Java configuration.
  6. [NOTE]
  7. ====
  8. Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] to demonstrate the use of Spring Security Java Configuration.
  9. ====
  10. [[jc-hello-wsca]]
  11. == Hello Web Security Java Configuration
  12. The first step is to create our Spring Security Java Configuration.
  13. The configuration creates a Servlet Filter known as the `springSecurityFilterChain`, which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application.
  14. The following example shows the most basic example of a Spring Security Java Configuration:
  15. [source,java]
  16. ----
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.context.annotation.*;
  19. import org.springframework.security.config.annotation.authentication.builders.*;
  20. import org.springframework.security.config.annotation.web.configuration.*;
  21. @Configuration
  22. @EnableWebSecurity
  23. public class WebSecurityConfig {
  24. @Bean
  25. public UserDetailsService userDetailsService() {
  26. InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
  27. manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
  28. return manager;
  29. }
  30. }
  31. ----
  32. This configuration is not complex or extensive, but it does a lot:
  33. * Require authentication to every URL in your application
  34. * Generate a login form for you
  35. * Let the user with a *Username* of `user` and a *Password* of `password` authenticate with form based authentication
  36. * Let the user logout
  37. * https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention
  38. * https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection
  39. * Security Header integration:
  40. ** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests
  41. ** https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration
  42. ** Cache Control (which you can override later in your application to allow caching of your static resources)
  43. ** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration
  44. ** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking]
  45. * Integration with the following Servlet API methods:
  46. ** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`]
  47. ** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest#getUserPrincipal()`]
  48. ** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest#isUserInRole(java.lang.String)`]
  49. ** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest#login(java.lang.String, java.lang.String)`]
  50. ** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest#logout()`]
  51. === AbstractSecurityWebApplicationInitializer
  52. The next step is to register the `springSecurityFilterChain` with the WAR file.
  53. You can do so in Java configuration with {spring-framework-reference-url}web/webmvc/mvc-servlet/container-config.html[Spring's `WebApplicationInitializer` support] in a Servlet 3.0+ environment.
  54. Not surprisingly, Spring Security provides a base class (`AbstractSecurityWebApplicationInitializer`) to ensure that the `springSecurityFilterChain` gets registered for you.
  55. The way in which we use `AbstractSecurityWebApplicationInitializer` differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application.
  56. * <<abstractsecuritywebapplicationinitializer-without-existing-spring>> - Use these instructions if you are not already using Spring
  57. * <<abstractsecuritywebapplicationinitializer-with-spring-mvc>> - Use these instructions if you are already using Spring
  58. [[abstractsecuritywebapplicationinitializer-without-existing-spring]]
  59. === AbstractSecurityWebApplicationInitializer without Existing Spring
  60. If you are not using Spring or Spring MVC, you need to pass the `WebSecurityConfig` to the superclass to ensure the configuration is picked up:
  61. [source,java]
  62. ----
  63. import org.springframework.security.web.context.*;
  64. public class SecurityWebApplicationInitializer
  65. extends AbstractSecurityWebApplicationInitializer {
  66. public SecurityWebApplicationInitializer() {
  67. super(WebSecurityConfig.class);
  68. }
  69. }
  70. ----
  71. The `SecurityWebApplicationInitializer`:
  72. * Automatically registers the `springSecurityFilterChain` Filter for every URL in your application.
  73. * Add a `ContextLoaderListener` that loads the <<jc-hello-wsca,WebSecurityConfig>>.
  74. [[abstractsecuritywebapplicationinitializer-with-spring-mvc]]
  75. === AbstractSecurityWebApplicationInitializer with Spring MVC
  76. If we use Spring elsewhere in our application, we probably already have a `WebApplicationInitializer` that is loading our Spring Configuration.
  77. If we use the previous configuration, we would get an error.
  78. Instead, we should register Spring Security with the existing `ApplicationContext`.
  79. For example, if we use Spring MVC, our `SecurityWebApplicationInitializer` could look something like the following:
  80. [source,java]
  81. ----
  82. import org.springframework.security.web.context.*;
  83. public class SecurityWebApplicationInitializer
  84. extends AbstractSecurityWebApplicationInitializer {
  85. }
  86. ----
  87. This only registers the `springSecurityFilterChain` for every URL in your application.
  88. After that, we need to ensure that `WebSecurityConfig` was loaded in our existing `ApplicationInitializer`.
  89. For example, if we use Spring MVC it is added in the `getServletConfigClasses()`:
  90. [[message-web-application-inititializer-java]]
  91. [source,java]
  92. ----
  93. public class MvcWebApplicationInitializer extends
  94. AbstractAnnotationConfigDispatcherServletInitializer {
  95. @Override
  96. protected Class<?>[] getServletConfigClasses() {
  97. return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
  98. }
  99. // ... other overrides ...
  100. }
  101. ----
  102. The reason for this is that Spring Security needs to be able to inspect some Spring MVC configuration in order to appropriately configure xref:servlet/authorization/authorize-http-requests.adoc#authorizing-endpoints[underlying request matchers], so they need to be in the same application context.
  103. Placing Spring Security in `getRootConfigClasses` places it into a parent application context that may not be able to find Spring MVC's `PathPatternParser`.
  104. ==== Configuring for Multiple Spring MVC Dispatchers
  105. If desired, any Spring Security configuration that is unrelated to Spring MVC may be placed in a different configuration class like so:
  106. [source,java]
  107. ----
  108. public class MvcWebApplicationInitializer extends
  109. AbstractAnnotationConfigDispatcherServletInitializer {
  110. @Override
  111. protected Class<?>[] getRootConfigClasses() {
  112. return new Class[] { NonWebSecurityConfig.class };
  113. }
  114. @Override
  115. protected Class<?>[] getServletConfigClasses() {
  116. return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
  117. }
  118. // ... other overrides ...
  119. }
  120. ----
  121. This can be helpful if you have multiple instances of `AbstractAnnotationConfigDispatcherServletInitializer` and don't want to duplicate the general security configuration across both of them.
  122. [[jc-httpsecurity]]
  123. == HttpSecurity
  124. Thus far, our <<jc-hello-wsca,`WebSecurityConfig`>> contains only information about how to authenticate our users.
  125. How does Spring Security know that we want to require all users to be authenticated?
  126. How does Spring Security know we want to support form-based authentication?
  127. Actually, there is a configuration class (called `SecurityFilterChain`) that is being invoked behind the scenes.
  128. It is configured with the following default implementation:
  129. [source,java]
  130. ----
  131. @Bean
  132. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  133. http
  134. .authorizeHttpRequests((authorize) -> authorize
  135. .anyRequest().authenticated()
  136. )
  137. .formLogin(Customizer.withDefaults())
  138. .httpBasic(Customizer.withDefaults());
  139. return http.build();
  140. }
  141. ----
  142. The default configuration (shown in the preceding example):
  143. * Ensures that any request to our application requires the user to be authenticated
  144. * Lets users authenticate with form-based login
  145. * Lets users authenticate with HTTP Basic authentication
  146. Note that this configuration parallels the XML namespace configuration:
  147. [source,xml]
  148. ----
  149. <http>
  150. <intercept-url pattern="/**" access="authenticated"/>
  151. <form-login />
  152. <http-basic />
  153. </http>
  154. ----
  155. === Multiple HttpSecurity Instances
  156. To effectively manage security in an application where certain areas need different protection, we can employ multiple filter chains alongside the `securityMatcher` DSL method.
  157. This approach allows us to define distinct security configurations tailored to specific parts of the application, enhancing overall application security and control.
  158. We can configure multiple `HttpSecurity` instances just as we can have multiple `<http>` blocks in XML.
  159. The key is to register multiple `SecurityFilterChain` ``@Bean``s.
  160. The following example has a different configuration for URLs that begin with `/api/`:
  161. [[multiple-httpsecurity-instances-java]]
  162. [source,java]
  163. ----
  164. @Configuration
  165. @EnableWebSecurity
  166. public class MultiHttpSecurityConfig {
  167. @Bean <1>
  168. public UserDetailsService userDetailsService() throws Exception {
  169. UserBuilder users = User.withDefaultPasswordEncoder();
  170. InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
  171. manager.createUser(users.username("user").password("password").roles("USER").build());
  172. manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
  173. return manager;
  174. }
  175. @Bean
  176. @Order(1) <2>
  177. public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
  178. http
  179. .securityMatcher("/api/**") <3>
  180. .authorizeHttpRequests((authorize) -> authorize
  181. .anyRequest().hasRole("ADMIN")
  182. )
  183. .httpBasic(Customizer.withDefaults());
  184. return http.build();
  185. }
  186. @Bean <4>
  187. public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
  188. http
  189. .authorizeHttpRequests((authorize) -> authorize
  190. .anyRequest().authenticated()
  191. )
  192. .formLogin(Customizer.withDefaults());
  193. return http.build();
  194. }
  195. }
  196. ----
  197. <1> Configure Authentication as usual.
  198. <2> Create an instance of `SecurityFilterChain` that contains `@Order` to specify which `SecurityFilterChain` should be considered first.
  199. <3> The `http.securityMatcher()` states that this `HttpSecurity` is applicable only to URLs that begin with `/api/`.
  200. <4> Create another instance of `SecurityFilterChain`.
  201. If the URL does not begin with `/api/`, this configuration is used.
  202. This configuration is considered after `apiFilterChain`, since it has an `@Order` value after `1` (no `@Order` defaults to last).
  203. === Choosing `securityMatcher` or `requestMatchers`
  204. A common question is:
  205. > What is the difference between the `http.securityMatcher()` method and `requestMatchers()` used for request authorization (i.e. inside of `http.authorizeHttpRequests()`)?
  206. To answer this question, it helps to understand that each `HttpSecurity` instance used to build a `SecurityFilterChain` contains a `RequestMatcher` to match incoming requests.
  207. If a request does not match a `SecurityFilterChain` with higher priority (e.g. `@Order(1)`), the request can be tried against a filter chain with lower priority (e.g. no `@Order`).
  208. [NOTE]
  209. ====
  210. The matching logic for multiple filter chains is performed by the xref:servlet/architecture.adoc#servlet-filterchainproxy[`FilterChainProxy`].
  211. ====
  212. The default `RequestMatcher` matches *any request* to ensure Spring Security protects *all requests by default*.
  213. [NOTE]
  214. ====
  215. Specifying a `securityMatcher` overrides this default.
  216. ====
  217. [WARNING]
  218. ====
  219. If no filter chain matches a particular request, the request is *not protected* by Spring Security.
  220. ====
  221. The following example demonstrates a single filter chain that only protects requests that begin with `/secured/`:
  222. [[choosing-security-matcher-request-matchers-java]]
  223. [source,java]
  224. ----
  225. @Configuration
  226. @EnableWebSecurity
  227. public class PartialSecurityConfig {
  228. @Bean
  229. public UserDetailsService userDetailsService() throws Exception {
  230. // ...
  231. }
  232. @Bean
  233. public SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {
  234. http
  235. .securityMatcher("/secured/**") <1>
  236. .authorizeHttpRequests((authorize) -> authorize
  237. .requestMatchers("/secured/user").hasRole("USER") <2>
  238. .requestMatchers("/secured/admin").hasRole("ADMIN") <3>
  239. .anyRequest().authenticated() <4>
  240. )
  241. .httpBasic(Customizer.withDefaults())
  242. .formLogin(Customizer.withDefaults());
  243. return http.build();
  244. }
  245. }
  246. ----
  247. <1> Requests that begin with `/secured/` will be protected but any other requests are not protected.
  248. <2> Requests to `/secured/user` require the `ROLE_USER` authority.
  249. <3> Requests to `/secured/admin` require the `ROLE_ADMIN` authority.
  250. <4> Any other requests (such as `/secured/other`) simply require an authenticated user.
  251. [TIP]
  252. ====
  253. It is _recommended_ to provide a `SecurityFilterChain` that does not specify any `securityMatcher` to ensure the entire application is protected, as demonstrated in the <<multiple-httpsecurity-instances-java,earlier example>>.
  254. ====
  255. Notice that the `requestMatchers` method only applies to individual authorization rules.
  256. Each request listed there must also match the overall `securityMatcher` for this particular `HttpSecurity` instance used to create the `SecurityFilterChain`.
  257. Using `anyRequest()` in this example matches all other requests within this particular `SecurityFilterChain` (which must begin with `/secured/`).
  258. [NOTE]
  259. ====
  260. See xref:servlet/authorization/authorize-http-requests.adoc[Authorize HttpServletRequests] for more information on `requestMatchers`.
  261. ====
  262. === `SecurityFilterChain` Endpoints
  263. Several filters in the `SecurityFilterChain` directly provide endpoints, such as the `UsernamePasswordAuthenticationFilter` which is set up by `http.formLogin()` and provides the `POST /login` endpoint.
  264. In the <<choosing-security-matcher-request-matchers-java,above example>>, the `/login` endpoint is not matched by `http.securityMatcher("/secured/**")` and therefore that application would not have any `GET /login` or `POST /login` endpoint.
  265. Such requests would return `404 Not Found`.
  266. This is often surprising to users.
  267. Specifying `http.securityMatcher()` affects what requests are matched by that `SecurityFilterChain`.
  268. However, it does not automatically affect endpoints provided by the filter chain.
  269. In such cases, you may need to customize the URL of any endpoints you would like the filter chain to provide.
  270. The following example demonstrates a configuration that secures requests that begin with `/secured/` and denies all other requests, while also customizing endpoints provided by the `SecurityFilterChain`:
  271. [[security-filter-chain-endpoints-java]]
  272. [source,java]
  273. ----
  274. @Configuration
  275. @EnableWebSecurity
  276. public class SecuredSecurityConfig {
  277. @Bean
  278. public UserDetailsService userDetailsService() throws Exception {
  279. // ...
  280. }
  281. @Bean
  282. @Order(1)
  283. public SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {
  284. http
  285. .securityMatcher("/secured/**") <1>
  286. .authorizeHttpRequests((authorize) -> authorize
  287. .anyRequest().authenticated() <2>
  288. )
  289. .formLogin((formLogin) -> formLogin <3>
  290. .loginPage("/secured/login")
  291. .loginProcessingUrl("/secured/login")
  292. .permitAll()
  293. )
  294. .logout((logout) -> logout <4>
  295. .logoutUrl("/secured/logout")
  296. .logoutSuccessUrl("/secured/login?logout")
  297. .permitAll()
  298. )
  299. .formLogin(Customizer.withDefaults());
  300. return http.build();
  301. }
  302. @Bean
  303. public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
  304. http
  305. .authorizeHttpRequests((authorize) -> authorize
  306. .anyRequest().denyAll() <5>
  307. );
  308. return http.build();
  309. }
  310. }
  311. ----
  312. <1> Requests that begin with `/secured/` will be protected by this filter chain.
  313. <2> Requests that begin with `/secured/` require an authenticated user.
  314. <3> Customize form login to prefix URLs with `/secured/`.
  315. <4> Customize logout to prefix URLs with `/secured/`.
  316. <5> All other requests will be denied.
  317. [NOTE]
  318. ====
  319. This example customizes the login and logout pages, which disables Spring Security's generated pages.
  320. You must xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form-custom[provide your own] custom endpoints for `GET /secured/login` and `GET /secured/logout`.
  321. Note that Spring Security still provides `POST /secured/login` and `POST /secured/logout` endpoints for you.
  322. ====
  323. === Real World Example
  324. The following example demonstrates a slightly more real-world configuration putting all of these elements together:
  325. [[real-world-example-java]]
  326. [source,java]
  327. ----
  328. @Configuration
  329. @EnableWebSecurity
  330. public class BankingSecurityConfig {
  331. @Bean <1>
  332. public UserDetailsService userDetailsService() {
  333. UserBuilder users = User.withDefaultPasswordEncoder();
  334. InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
  335. manager.createUser(users.username("user1").password("password").roles("USER", "VIEW_BALANCE").build());
  336. manager.createUser(users.username("user2").password("password").roles("USER").build());
  337. manager.createUser(users.username("admin").password("password").roles("ADMIN").build());
  338. return manager;
  339. }
  340. @Bean
  341. @Order(1) <2>
  342. public SecurityFilterChain approvalsSecurityFilterChain(HttpSecurity http) throws Exception {
  343. String[] approvalsPaths = { "/accounts/approvals/**", "/loans/approvals/**", "/credit-cards/approvals/**" };
  344. http
  345. .securityMatcher(approvalsPaths)
  346. .authorizeHttpRequests((authorize) -> authorize
  347. .anyRequest().hasRole("ADMIN")
  348. )
  349. .httpBasic(Customizer.withDefaults());
  350. return http.build();
  351. }
  352. @Bean
  353. @Order(2) <3>
  354. public SecurityFilterChain bankingSecurityFilterChain(HttpSecurity http) throws Exception {
  355. String[] bankingPaths = { "/accounts/**", "/loans/**", "/credit-cards/**", "/balances/**" };
  356. String[] viewBalancePaths = { "/balances/**" };
  357. http
  358. .securityMatcher(bankingPaths)
  359. .authorizeHttpRequests((authorize) -> authorize
  360. .requestMatchers(viewBalancePaths).hasRole("VIEW_BALANCE")
  361. .anyRequest().hasRole("USER")
  362. );
  363. return http.build();
  364. }
  365. @Bean <4>
  366. public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
  367. String[] allowedPaths = { "/", "/user-login", "/user-logout", "/notices", "/contact", "/register" };
  368. http
  369. .authorizeHttpRequests((authorize) -> authorize
  370. .requestMatchers(allowedPaths).permitAll()
  371. .anyRequest().authenticated()
  372. )
  373. .formLogin((formLogin) -> formLogin
  374. .loginPage("/user-login")
  375. .loginProcessingUrl("/user-login")
  376. )
  377. .logout((logout) -> logout
  378. .logoutUrl("/user-logout")
  379. .logoutSuccessUrl("/?logout")
  380. );
  381. return http.build();
  382. }
  383. }
  384. ----
  385. <1> Begin by configuring authentication settings.
  386. <2> Define a `SecurityFilterChain` instance with `@Order(1)`, which means that this filter chain will have the highest priority.
  387. This filter chain applies only to requests that begin with `/accounts/approvals/`, `/loans/approvals/` or `/credit-cards/approvals/`.
  388. Requests to this filter chain require the `ROLE_ADMIN` authority and allow HTTP Basic Authentication.
  389. <3> Next, create another `SecurityFilterChain` instance with `@Order(2)` which will be considered second.
  390. This filter chain applies only to requests that begin with `/accounts/`, `/loans/`, `/credit-cards/`, or `/balances/`.
  391. Notice that because this filter chain is second, any requests that include `/approvals/` will match the previous filter chain and will *not* be matched by this filter chain.
  392. Requests to this filter chain require the `ROLE_USER` authority.
  393. This filter chain does not define any authentication because the next (default) filter chain contains that configuration.
  394. <4> Lastly, create an additional `SecurityFilterChain` instance without an `@Order` annotation.
  395. This configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).
  396. Requests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.
  397. Any other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.
  398. [[jc-custom-dsls]]
  399. == Custom DSLs
  400. You can provide your own custom DSLs in Spring Security:
  401. [tabs]
  402. ======
  403. Java::
  404. +
  405. [source,java,role="primary"]
  406. ----
  407. public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
  408. private boolean flag;
  409. @Override
  410. public void init(HttpSecurity http) throws Exception {
  411. // any method that adds another configurer
  412. // must be done in the init method
  413. http.csrf().disable();
  414. }
  415. @Override
  416. public void configure(HttpSecurity http) throws Exception {
  417. ApplicationContext context = http.getSharedObject(ApplicationContext.class);
  418. // here we lookup from the ApplicationContext. You can also just create a new instance.
  419. MyFilter myFilter = context.getBean(MyFilter.class);
  420. myFilter.setFlag(flag);
  421. http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
  422. }
  423. public MyCustomDsl flag(boolean value) {
  424. this.flag = value;
  425. return this;
  426. }
  427. public static MyCustomDsl customDsl() {
  428. return new MyCustomDsl();
  429. }
  430. }
  431. ----
  432. Kotlin::
  433. +
  434. [source,kotlin,role="secondary"]
  435. ----
  436. class MyCustomDsl : AbstractHttpConfigurer<MyCustomDsl, HttpSecurity>() {
  437. var flag: Boolean = false
  438. override fun init(http: HttpSecurity) {
  439. // any method that adds another configurer
  440. // must be done in the init method
  441. http.csrf().disable()
  442. }
  443. override fun configure(http: HttpSecurity) {
  444. val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
  445. // here we lookup from the ApplicationContext. You can also just create a new instance.
  446. val myFilter: MyFilter = context.getBean(MyFilter::class.java)
  447. myFilter.setFlag(flag)
  448. http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter::class.java)
  449. }
  450. companion object {
  451. @JvmStatic
  452. fun customDsl(): MyCustomDsl {
  453. return MyCustomDsl()
  454. }
  455. }
  456. }
  457. ----
  458. ======
  459. [NOTE]
  460. ====
  461. This is actually how methods like `HttpSecurity.authorizeHttpRequests()` are implemented.
  462. ====
  463. You can then use the custom DSL:
  464. [tabs]
  465. ======
  466. Java::
  467. +
  468. [source,java,role="primary"]
  469. ----
  470. @Configuration
  471. @EnableWebSecurity
  472. public class Config {
  473. @Bean
  474. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  475. http
  476. .with(MyCustomDsl.customDsl(), (dsl) -> dsl
  477. .flag(true)
  478. )
  479. // ...
  480. return http.build();
  481. }
  482. }
  483. ----
  484. Kotlin::
  485. +
  486. [source,kotlin,role="secondary"]
  487. ----
  488. @Configuration
  489. @EnableWebSecurity
  490. class Config {
  491. @Bean
  492. fun filterChain(http: HttpSecurity): SecurityFilterChain {
  493. http
  494. .with(MyCustomDsl.customDsl()) {
  495. flag = true
  496. }
  497. // ...
  498. return http.build()
  499. }
  500. }
  501. ----
  502. ======
  503. The code is invoked in the following order:
  504. * Code in the `Config.filterChain` method is invoked
  505. * Code in the `MyCustomDsl.init` method is invoked
  506. * Code in the `MyCustomDsl.configure` method is invoked
  507. If you want, you can have `HttpSecurity` add `MyCustomDsl` by default by using `SpringFactories`.
  508. For example, you can create a resource on the classpath named `META-INF/spring.factories` with the following contents:
  509. .META-INF/spring.factories
  510. [source]
  511. ----
  512. org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
  513. ----
  514. You can also explicit disable the default:
  515. [tabs]
  516. ======
  517. Java::
  518. +
  519. [source,java,role="primary"]
  520. ----
  521. @Configuration
  522. @EnableWebSecurity
  523. public class Config {
  524. @Bean
  525. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  526. http
  527. .with(MyCustomDsl.customDsl(), (dsl) -> dsl
  528. .disable()
  529. )
  530. ...;
  531. return http.build();
  532. }
  533. }
  534. ----
  535. Kotlin::
  536. +
  537. [source,kotlin,role="secondary"]
  538. ----
  539. @Configuration
  540. @EnableWebSecurity
  541. class Config {
  542. @Bean
  543. fun filterChain(http: HttpSecurity): SecurityFilterChain {
  544. http
  545. .with(MyCustomDsl.customDsl()) {
  546. disable()
  547. }
  548. // ...
  549. return http.build()
  550. }
  551. }
  552. ----
  553. ======
  554. [[post-processing-configured-objects]]
  555. == Post Processing Configured Objects
  556. Spring Security's Java configuration does not expose every property of every object that it configures.
  557. This simplifies the configuration for a majority of users.
  558. After all, if every property were exposed, users could use standard bean configuration.
  559. While there are good reasons to not directly expose every property, users may still need more advanced configuration options.
  560. To address this issue, Spring Security introduces the concept of an `ObjectPostProcessor`, which can be used to modify or replace many of the `Object` instances created by the Java Configuration.
  561. For example, to configure the `filterSecurityPublishAuthorizationSuccess` property on `FilterSecurityInterceptor`, you can use the following:
  562. [source,java]
  563. ----
  564. @Bean
  565. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  566. http
  567. .authorizeHttpRequests((authorize) -> authorize
  568. .anyRequest().authenticated()
  569. .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
  570. public <O extends FilterSecurityInterceptor> O postProcess(
  571. O fsi) {
  572. fsi.setPublishAuthorizationSuccess(true);
  573. return fsi;
  574. }
  575. })
  576. );
  577. return http.build();
  578. }
  579. ----