Add Modular Spring Security Configuration
Closes gh-16258
This commit is contained in:
@@ -242,3 +242,72 @@ It matches the requests in order by the `securityMatcher` definition.
|
||||
In this case, that means that, if the URL path starts with `/api`, Spring Security uses `apiHttpSecurity`.
|
||||
If the URL does not start with `/api`, Spring Security defaults to `webHttpSecurity`, which has an implied `securityMatcher` that matches any request.
|
||||
|
||||
|
||||
[[modular-serverhttpsecurity-configuration]]
|
||||
== Modular ServerHttpSecurity Configuration
|
||||
|
||||
Many users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it within the `SecurityWebFilterChain` Bean declaration.
|
||||
However, there are times that users may want to modularize the configuration.
|
||||
This can be done using:
|
||||
|
||||
* xref:#serverhttpsecurity-customizer-bean[Customizer<ServerHttpSecurity> Beans]
|
||||
* xref:#top-level-customizer-bean[Top Level ServerHttpSecurity Customizer Beans]
|
||||
|
||||
// FIXME: this needs to link to appropriate spot
|
||||
// NOTE: If you are using Spring Security's xref:servlet/configuration/kotlin.adoc[], then you can also expose `*Dsl -> Unit` Beans as outlined in xref:./kotlin.adoc#modular-httpsecuritydsl-configuration[Modular HttpSecurityDsl Configuration].
|
||||
|
||||
|
||||
[[serverhttpsecurity-customizer-bean]]
|
||||
=== Customizer<ServerHttpSecurity> Beans
|
||||
|
||||
If you would like to modularize your security configuration you can place logic in a `Customizer<ServerHttpSecurity>` Bean.
|
||||
For example, the following configuration will ensure all `ServerHttpSecurity` instances are configured to:
|
||||
|
||||
include-code::./ServerHttpSecurityCustomizerBeanConfiguration[tag=httpSecurityCustomizer,indent=0]
|
||||
|
||||
<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`
|
||||
<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]
|
||||
|
||||
|
||||
[[top-level-customizer-bean]]
|
||||
=== Top Level ServerHttpSecurity Customizer Beans
|
||||
|
||||
If you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level `HttpSecurity` `Customizer` Beans.
|
||||
|
||||
A top level `HttpSecurity` `Customizer` type can be summarized as any `Customizer<T>` that matches `public HttpSecurity.*(Customizer<T>)`.
|
||||
This translates to any `Customizer<T>` that is a single argument to a public method on javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[].
|
||||
|
||||
A few examples can help to clarify.
|
||||
If `Customizer<ContentTypeOptionsConfig>` is published as a Bean, it will not be be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#contentTypeOptions(org.springframework.security.config.Customizer)[] which is not a method defined on `HttpSecurity`.
|
||||
However, if `Customizer<HeadersConfigurer<HttpSecurity>>` is published as a Bean, it will be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#headers(org.springframework.security.config.Customizer)[].
|
||||
|
||||
For example, the following configuration will ensure that the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] is set to `object-src 'none'`:
|
||||
|
||||
include-code::./TopLevelCustomizerBeanConfiguration[tag=headersCustomizer,indent=0]
|
||||
|
||||
[[customizer-bean-ordering]]
|
||||
=== Customizer Bean Ordering
|
||||
|
||||
First each xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Bean] is applied using https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].
|
||||
This means that if there are multiple `Customizer<HttpSecurity>` Beans, the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.
|
||||
|
||||
Next every xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.
|
||||
If there is are two `Customizer<HeadersConfigurer<HttpSecurity>>` beans and two `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` instances, the order that each `Customizer` type is invoked is undefined.
|
||||
However, the order that each instance of `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.
|
||||
|
||||
Finally, the `HttpSecurity` Bean is injected as a Bean.
|
||||
All `Customizer` instances are applied before the `HttpSecurity` Bean is created.
|
||||
This allows overriding the customizations provided by the `Customizer` Beans.
|
||||
|
||||
You can find an example below that illustrates the ordering:
|
||||
|
||||
include-code::./CustomizerBeanOrderingConfiguration[tag=sample,indent=0]
|
||||
|
||||
<1> First all `Customizer<HttpSecurity>` instances are applied.
|
||||
The `adminAuthorization` Bean has the highest `@Order` so it is applied first.
|
||||
If there are no `@Order` annotations on the `Customizer<HttpSecurity>` Beans or the `@Order` annotations had the same value, then the order that the `Customizer<HttpSecurity>` instances are applied is undefined.
|
||||
<2> The `userAuthorization` is applied next due to being an instance of `Customizer<HttpSecurity>`
|
||||
<3> The order that the `Customizer` types are undefined.
|
||||
In this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.
|
||||
If `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `Customizer<HeadersConfigurer<HttpSecurity>>` Beans.
|
||||
<4> After all of the `Customizer` Beans are applied, the `HttpSecurity` is passed in as a Bean.
|
||||
|
||||
@@ -664,6 +664,75 @@ class Config {
|
||||
----
|
||||
======
|
||||
|
||||
[[modular-httpsecurity-configuration]]
|
||||
== Modular HttpSecurity Configuration
|
||||
|
||||
Many users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it in a single `SecurityFilterChain` instance.
|
||||
However, there are times that users may want to modularize the configuration.
|
||||
This can be done using:
|
||||
|
||||
* xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Beans]
|
||||
* xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans]
|
||||
|
||||
NOTE: If you are using Spring Security's xref:servlet/configuration/kotlin.adoc[], then you can also expose `*Dsl -> Unit` Beans as outlined in xref:./kotlin.adoc#modular-httpsecuritydsl-configuration[Modular HttpSecurityDsl Configuration].
|
||||
|
||||
|
||||
[[httpsecurity-customizer-bean]]
|
||||
=== Customizer<HttpSecurity> Beans
|
||||
|
||||
If you would like to modularize your security configuration you can place logic in a `Customizer<HttpSecurity>` Bean.
|
||||
For example, the following configuration will ensure all `HttpSecurity` instances are configured to:
|
||||
|
||||
include-code::./HttpSecurityCustomizerBeanConfiguration[tag=httpSecurityCustomizer,indent=0]
|
||||
|
||||
<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`
|
||||
<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]
|
||||
|
||||
|
||||
[[top-level-customizer-bean]]
|
||||
=== Top Level HttpSecurity Customizer Beans
|
||||
|
||||
If you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level `HttpSecurity` `Customizer` Beans.
|
||||
|
||||
A top level `HttpSecurity` `Customizer` type can be summarized as any `Customizer<T>` that matches `public HttpSecurity.*(Customizer<T>)`.
|
||||
This translates to any `Customizer<T>` that is a single argument to a public method on javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[].
|
||||
|
||||
A few examples can help to clarify.
|
||||
If `Customizer<ContentTypeOptionsConfig>` is published as a Bean, it will not be be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#contentTypeOptions(org.springframework.security.config.Customizer)[] which is not a method defined on `HttpSecurity`.
|
||||
However, if `Customizer<HeadersConfigurer<HttpSecurity>>` is published as a Bean, it will be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#headers(org.springframework.security.config.Customizer)[].
|
||||
|
||||
For example, the following configuration will ensure that the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] is set to `object-src 'none'`:
|
||||
|
||||
include-code::./TopLevelCustomizerBeanConfiguration[tag=headersCustomizer,indent=0]
|
||||
|
||||
[[customizer-bean-ordering]]
|
||||
=== Customizer Bean Ordering
|
||||
|
||||
First each xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Bean] is applied using https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].
|
||||
This means that if there are multiple `Customizer<HttpSecurity>` Beans, the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.
|
||||
|
||||
Next every xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.
|
||||
If there is are two `Customizer<HeadersConfigurer<HttpSecurity>>` beans and two `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` instances, the order that each `Customizer` type is invoked is undefined.
|
||||
However, the order that each instance of `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.
|
||||
|
||||
Finally, the `HttpSecurity` Bean is injected as a Bean.
|
||||
All `Customizer` instances are applied before the `HttpSecurity` Bean is created.
|
||||
This allows overriding the customizations provided by the `Customizer` Beans.
|
||||
|
||||
You can find an example below that illustrates the ordering:
|
||||
|
||||
include-code::./CustomizerBeanOrderingConfiguration[tag=sample,indent=0]
|
||||
|
||||
<1> First all `Customizer<HttpSecurity>` instances are applied.
|
||||
The `adminAuthorization` Bean has the highest `@Order` so it is applied first.
|
||||
If there are no `@Order` annotations on the `Customizer<HttpSecurity>` Beans or the `@Order` annotations had the same value, then the order that the `Customizer<HttpSecurity>` instances are applied is undefined.
|
||||
<2> The `userAuthorization` is applied next due to being an instance of `Customizer<HttpSecurity>`
|
||||
<3> The order that the `Customizer` types are undefined.
|
||||
In this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.
|
||||
If `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `Customizer<HeadersConfigurer<HttpSecurity>>` Beans.
|
||||
<4> After all of the `Customizer` Beans are applied, the `HttpSecurity` is passed in as a Bean.
|
||||
|
||||
|
||||
[[post-processing-configured-objects]]
|
||||
== Post Processing Configured Objects
|
||||
|
||||
|
||||
@@ -346,3 +346,76 @@ class BankingSecurityConfig {
|
||||
This configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).
|
||||
Requests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.
|
||||
Any other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.
|
||||
|
||||
|
||||
[[modular-httpsecuritydsl-configuration]]
|
||||
== Modular HttpSecurityDsl Configuration
|
||||
|
||||
Many users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it in a single `SecurityFilterChain` instance.
|
||||
However, there are times that users may want to modularize the configuration.
|
||||
This can be done using:
|
||||
|
||||
* xref:#httpsecuritydsl-bean[HttpSecurityDsl.() -> Unit Beans]
|
||||
* xref:#top-level-dsl-bean[Top Level Security Dsl Beans]
|
||||
|
||||
NOTE: Since the Spring Security Kotlin Dsl (`HttpSecurityDsl`) uses `HttpSecurity`, all of the Java xref:./kotlin.adoc#modular-bean-configuration[Modular Bean Customization] is applied before xref:#modular-httpsecuritydsl-configuration[Modular HttpSecurity Configuration].
|
||||
|
||||
[[httpsecuritydsl-bean]]
|
||||
=== HttpSecurityDsl.() -> Unit Beans
|
||||
|
||||
If you would like to modularize your security configuration you can place logic in a `HttpSecurityDsl.() -> Unit` Bean.
|
||||
For example, the following configuration will ensure all `HttpSecurityDsl` instances are configured to:
|
||||
|
||||
include-code::./HttpSecurityDslBeanConfiguration[tag=httpSecurityDslBean,indent=0]
|
||||
|
||||
<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`
|
||||
<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]
|
||||
|
||||
|
||||
[[top-level-dsl-bean]]
|
||||
=== Top Level Security Dsl Beans
|
||||
|
||||
If you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level Security Dsl Beans.
|
||||
|
||||
A top level Security Dsl can be summarized as any class Dsl class that matches `public HttpSecurityDsl.*(<Dsl>)`.
|
||||
This translates to any Security Dsl that is a single argument to a public method on `HttpSecurityDsl`.
|
||||
|
||||
A few examples can help to clarify.
|
||||
If `ContentTypeOptionsDsl.() -> Unit` is published as a Bean, it will not be be automatically applied because it is an argument to `HeadersDsl#contentTypeOptions(ContentTypeOptionsDsl.() -> Unit)` and is not an argument to a method defined on `HttpSecurityDsl`.
|
||||
However, if `HeadersDsl.() -> Unit` is published as a Bean, it will be automatically applied because it is an argument to `HttpSecurityDsl.headers(HeadersDsl.() -> Unit)`.
|
||||
|
||||
For example, the following configuration ensure all `HttpSecurityDsl` instances are configured to:
|
||||
|
||||
include-code::./TopLevelDslBeanConfiguration[tag=headersSecurity,indent=0]
|
||||
|
||||
<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`
|
||||
|
||||
[[dsl-bean-ordering]]
|
||||
=== Dsl Bean Ordering
|
||||
|
||||
First, all xref:servlet/configuration/java.adoc#modular-httpsecurity-configuration[Modular HttpSecurity Configuration] is applied since the Kotlin Dsl uses an `HttpSecurity` Bean.
|
||||
|
||||
Second, each xref:#httpsecuritydsl-bean[HttpSecurityDsl.() -> Unit Beans] is applied using https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].
|
||||
This means that if there are multiple `HttpSecurity.() -> Unit` Beans, the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.
|
||||
|
||||
Next, every xref:#top-level-dsl-bean[Top Level Security Dsl Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.
|
||||
If there is are differt types of top level security Beans (.e.g. `HeadersDsl.() -> Unit` and `HttpsRedirectDsl.() -> Unit`), then the order that each Dsl type is invoked is undefined.
|
||||
However, the order that each instance of of the same top level security Bean type is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.
|
||||
|
||||
Finally, the `HttpSecurityDsl` Bean is injected as a Bean.
|
||||
All `*Dsl.() -> Unit` Beans are applied before the `HttpSecurityDsl` Bean is created.
|
||||
This allows overriding the customizations provided by the `*Dsl.() -> Unit` Beans.
|
||||
|
||||
You can find an example below that illustrates the ordering:
|
||||
|
||||
include-code::./DslBeanOrderingConfiguration[tag=sample,indent=0]
|
||||
|
||||
<1> All xref:servlet/configuration/java.adoc#modular-httpsecurity-configuration[Modular HttpSecurity Configuration] is applied since the Kotlin Dsl uses an `HttpSecurity` Bean.
|
||||
<2> All `HttpSecurity.() -> Unit` instances are applied.
|
||||
The `adminAuthorization` Bean has the highest `@Order` so it is applied first.
|
||||
If there are no `@Order` annotations on the `HttpSecurity.() -> Unit` Beans or the `@Order` annotations had the same value, then the order that the `HttpSecurity.() -> Unit` instances are applied is undefined.
|
||||
<3> The `userAuthorization` is applied next due to being an instance of `HttpSecurity.() -> Unit`
|
||||
<4> The order that the `*Dsl.() -> Unit` types are undefined.
|
||||
In this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.
|
||||
If `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `HeadersDsl.() -> Unit` Beans.
|
||||
<5> After all of the `*Dsl.() -> Unit` Beans are applied, the `HttpSecurityDsl` is passed in as a Bean.
|
||||
|
||||
@@ -15,6 +15,7 @@ Each section that follows will indicate the more notable removals as well as the
|
||||
|
||||
== Config
|
||||
|
||||
* Support modular modular configuration in xref::servlet/configuration/java.adoc#modular-httpsecurity-configuration[Servlets] and xref::reactive/configuration/webflux.adoc#modular-serverhttpsecurity-configuration[WebFlux]
|
||||
* Removed `and()` from the `HttpSecurity` DSL in favor of using the lambda methods
|
||||
* Removed `authorizeRequests` in favor of `authorizeHttpRequests`
|
||||
* Simplified expression migration for `authorizeRequests`
|
||||
|
||||
Reference in New Issue
Block a user