mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 16:52:13 +00:00
Add 7.0 -> 8.0 Migration Guide
Closes gh-17182
This commit is contained in:
parent
215547f8c8
commit
37a814bc29
@ -2,14 +2,11 @@
|
||||
* xref:prerequisites.adoc[Prerequisites]
|
||||
* xref:community.adoc[Community]
|
||||
* xref:whats-new.adoc[What's New]
|
||||
* xref:migration-7/index.adoc[Preparing for 7.0]
|
||||
** xref:migration-7/authentication.adoc[Authentication]
|
||||
** xref:migration-7/authorization.adoc[Authorization]
|
||||
** xref:migration-7/configuration.adoc[Configuration]
|
||||
** xref:migration-7/ldap.adoc[LDAP]
|
||||
** xref:migration-7/oauth2.adoc[OAuth 2.0]
|
||||
** xref:migration-7/web.adoc[Web]
|
||||
* xref:migration/index.adoc[Migrating to 6]
|
||||
* xref:migration-8/index.adoc[Preparing for 8.0]
|
||||
* xref:migration/index.adoc[Migrating to 7]
|
||||
** xref:migration/servlet/index.adoc[Servlet]
|
||||
*** xref:migration/servlet/oauth2.adoc[OAuth 2.0]
|
||||
** xref:migration/reactive.adoc[Reactive]
|
||||
* xref:getting-spring-security.adoc[Getting Spring Security]
|
||||
* xref:attachment$api/java/index.html[Javadoc]
|
||||
* xref:features/index.adoc[Features]
|
||||
|
@ -1,68 +0,0 @@
|
||||
= Authentication Changes
|
||||
|
||||
== Opaque Token Credentials Will Be Encoded For You
|
||||
|
||||
In order to comply more closely with the Introspection RFC, Spring Security's opaque token support will encode the client id and secret before creating the authorization header.
|
||||
This change means you will no longer have to encode the client id and secret yourself.
|
||||
|
||||
If your client id or secret contain URL-unsafe characters, then you can prepare yourself for this change by doing the following:
|
||||
|
||||
=== Replace Usage of `introspectionClientCredentials`
|
||||
|
||||
Since Spring Security can now do the encoding for you, replace xref:servlet/oauth2/resource-server/opaque-token.adoc#oauth2resourceserver-opaque-introspectionuri-dsl[using `introspectionClientCredentials`] with publishing the following `@Bean`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
OpaqueTokenIntrospector introspector() {
|
||||
return SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)
|
||||
.clientId(unencodedClientId).clientSecret(unencodedClientSecret).build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun introspector(): OpaqueTokenIntrospector {
|
||||
return SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)
|
||||
.clientId(unencodedClientId).clientSecret(unencodedClientSecret).build()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The above will be the default in 7.0.
|
||||
|
||||
If this setting gives you trouble or you cannot apply it for now, you can use the `RestOperations` constructor instead:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
OpaqueTokenIntrospector introspector() {
|
||||
RestTemplate rest = new RestTemplate();
|
||||
rest.addInterceptor(new BasicAuthenticationInterceptor(encodedClientId, encodedClientSecret));
|
||||
return new SpringOpaqueTokenIntrospector(introspectionUri, rest);
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun introspector(): OpaqueTokenIntrospector {
|
||||
val rest = RestTemplate()
|
||||
rest.addInterceptor(BasicAuthenticationInterceptor(encodedClientId, encodedClientSecret))
|
||||
return SpringOpaqueTokenIntrospector(introspectionUri, rest)
|
||||
}
|
||||
----
|
||||
======
|
@ -1,24 +0,0 @@
|
||||
= Authorization Changes
|
||||
|
||||
The following sections relate to how to adapt to changes in the authorization support.
|
||||
|
||||
== Method Security
|
||||
|
||||
[[compile-with-parameters]]
|
||||
=== Compile With `-parameters`
|
||||
|
||||
Spring Framework 6.1 https://github.com/spring-projects/spring-framework/issues/29559[removes LocalVariableTableParameterNameDiscoverer].
|
||||
This affects how `@PreAuthorize` and other xref:servlet/authorization/method-security.adoc[method security] annotations will process parameter names.
|
||||
If you are using method security annotations with parameter names, for example:
|
||||
|
||||
[source,java]
|
||||
.Method security annotation using `id` parameter name
|
||||
----
|
||||
@PreAuthorize("@authz.checkPermission(#id, authentication)")
|
||||
public void doSomething(Long id) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
You must compile with `-parameters` to ensure that the parameter names are available at runtime.
|
||||
For more information about this, please visit the https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#core-container[Upgrading to Spring Framework 6.1 page].
|
@ -1,125 +0,0 @@
|
||||
= Configuration Migrations
|
||||
|
||||
The following steps relate to changes around how to configure `HttpSecurity`, `WebSecurity` and related components.
|
||||
|
||||
== Use the Lambda DSL
|
||||
|
||||
The Lambda DSL is present in Spring Security since version 5.2, and it allows HTTP security to be configured using lambdas.
|
||||
|
||||
You may have seen this style of configuration in the Spring Security documentation or samples.
|
||||
Let us take a look at how a lambda configuration of HTTP security compares to the previous configuration style.
|
||||
|
||||
[source,java]
|
||||
.Configuration using lambdas
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/blog/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(formLogin -> formLogin
|
||||
.loginPage("/login")
|
||||
.permitAll()
|
||||
)
|
||||
.rememberMe(Customizer.withDefaults());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
.Equivalent configuration without using lambdas
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests()
|
||||
.requestMatchers("/blog/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.permitAll()
|
||||
.and()
|
||||
.rememberMe();
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The Lambda DSL is the preferred way to configure Spring Security, the prior configuration style will not be valid in Spring Security 7 where the usage of the Lambda DSL will be required.
|
||||
This has been done mainly for a couple of reasons:
|
||||
|
||||
- The previous way it was not clear what object was getting configured without knowing what the return type was.
|
||||
The deeper the nesting the more confusing it became.
|
||||
Even experienced users would think that their configuration was doing one thing when in fact, it was doing something else.
|
||||
|
||||
- Consistency.
|
||||
Many code bases switched between the two styles which caused inconsistencies that made understanding the configuration difficult and often led to misconfigurations.
|
||||
|
||||
=== Lambda DSL Configuration Tips
|
||||
|
||||
When comparing the two samples above, you will notice some key differences:
|
||||
|
||||
- In the Lambda DSL there is no need to chain configuration options using the `.and()` method.
|
||||
The `HttpSecurity` instance is automatically returned for further configuration after the call to the lambda method.
|
||||
|
||||
- `Customizer.withDefaults()` enables a security feature using the defaults provided by Spring Security.
|
||||
This is a shortcut for the lambda expression `it -> {}`.
|
||||
|
||||
=== WebFlux Security
|
||||
|
||||
You may also configure WebFlux security using lambdas in a similar manner.
|
||||
Below is an example configuration using lambdas.
|
||||
|
||||
[source,java]
|
||||
.WebFlux configuration using lambdas
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange(exchanges -> exchanges
|
||||
.pathMatchers("/blog/**").permitAll()
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.formLogin(formLogin -> formLogin
|
||||
.loginPage("/login")
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
=== Goals of the Lambda DSL
|
||||
|
||||
The Lambda DSL was created to accomplish to following goals:
|
||||
|
||||
- Automatic indentation makes the configuration more readable.
|
||||
- There is no need to chain configuration options using `.and()`
|
||||
- The Spring Security DSL has a similar configuration style to other Spring DSLs such as Spring Integration and Spring Cloud Gateway.
|
||||
|
||||
== Use `.with()` instead of `.apply()` for Custom DSLs
|
||||
|
||||
In versions prior to 6.2, if you had a xref:servlet/configuration/java.adoc#jc-custom-dsls[custom DSL], you would apply it to the `HttpSecurity` using the `HttpSecurity#apply(...)` method.
|
||||
However, starting from version 6.2, this method is deprecated and will be removed in 7.0 because it will no longer be possible to chain configurations using `.and()` once `.and()` is removed (see https://github.com/spring-projects/spring-security/issues/13067).
|
||||
Instead, it is recommended to use the new `.with(...)` method.
|
||||
For more information about how to use `.with(...)` please refer to the xref:servlet/configuration/java.adoc#jc-custom-dsls[Custom DSLs section].
|
@ -1,11 +0,0 @@
|
||||
= LDAP Migrations
|
||||
|
||||
The following steps relate to changes around how to configure the LDAP components and how to use an embedded LDAP server.
|
||||
|
||||
== Use `UnboundId` instead of `ApacheDS`
|
||||
|
||||
ApacheDS has not had a GA release for a considerable period, and its classes in Spring Security were https://github.com/spring-projects/spring-security/pull/6376[deprecated in version 5.2].
|
||||
Consequently, support for ApacheDS will be discontinued in version 7.0.
|
||||
|
||||
If you are currently using ApacheDS as an embedded LDAP server, we recommend migrating to https://ldap.com/unboundid-ldap-sdk-for-java/[UnboundId].
|
||||
You can find instructions in xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap-embedded[this section] that describe how to set up an embedded UnboundId LDAP server.
|
@ -1,172 +0,0 @@
|
||||
= OAuth 2.0 Changes
|
||||
|
||||
== Validate `typ` Header with `JwtTypeValidator`
|
||||
|
||||
`NimbusJwtDecoder` in Spring Security 7 will move `typ` header validation to `JwtTypeValidator` instead of relying on Nimbus.
|
||||
This brings it in line with `NimbusJwtDecoder` validating claims instead of relying on Nimbus to validate them.
|
||||
|
||||
If you are changing Nimbus's default type validation in a `jwtProcessorCustomizer` method, then you should move that to `JwtTypeValidator` or an implementation of `OAuth2TokenValidator` of your own.
|
||||
|
||||
To check if you are prepared for this change, add the default `JwtTypeValidator` to your list of validators, as this will be included by default in 7:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
new JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - Switch off Nimbus verifying the `typ` (this will be off by default in 7)
|
||||
<2> - Add the default `typ` validator (this will be included by default in 7)
|
||||
|
||||
Note the default value verifies that the `typ` value either be `JWT` or not present, which is the same as the Nimbus default.
|
||||
It is also aligned with https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.9[RFC 7515] which states that `typ` is optional.
|
||||
|
||||
|
||||
=== I'm Using A `DefaultJOSEObjectTypeVerifier`
|
||||
|
||||
If you have something like the following in your configuration:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.jwtProcessorCustomizer((c) -> c
|
||||
.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>("JOSE"))
|
||||
)
|
||||
.build();
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.jwtProcessorCustomizer {
|
||||
it.setJWSTypeVerifier(DefaultJOSEObjectTypeVerifier("JOSE"))
|
||||
}
|
||||
.build()
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Then change this to:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
new JwtIssuerValidator(location), new JwtTypeValidator("JOSE")));
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false)
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
JwtIssuerValidator(location), JwtTypeValidator("JOSE")))
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
To indicate that the `typ` header is optional, use `#setAllowEmpty(true)` (this is the equivalent of including `null` in the list of allowed types in `DefaultJOSEObjectTypeVerifier`).
|
||||
|
||||
=== I want to opt-out
|
||||
|
||||
If you want to keep doing things the way that you are, then the steps are similar, just in reverse:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(true) <1>
|
||||
.jwtProcessorCustomizer((c) -> c
|
||||
.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>("JOSE"))
|
||||
)
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
|
||||
new JwtTimestampValidator(), new JwtIssuerValidator(location))); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(true) <1>
|
||||
.jwtProcessorCustomizer {
|
||||
it.setJWSTypeVerifier(DefaultJOSEObjectTypeVerifier("JOSE"))
|
||||
}
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(DelegatingOAuth2TokenValidator(
|
||||
JwtTimestampValidator(), JwtIssuerValidator(location))) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - leave Nimbus type verification on
|
||||
<2> - specify the list of validators you need, excluding `JwtTypeValidator`
|
||||
|
||||
For additional guidance, please see the xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-validation[JwtDecoder Validators] section in the reference.
|
@ -1,165 +0,0 @@
|
||||
= Saml 2.0 Migrations
|
||||
|
||||
== Continue Filter Chain When No Relying Party Found
|
||||
|
||||
In Spring Security 6, `Saml2WebSsoAuthenticationFilter` throws an exception when the request URI matches, but no relying party registration is found.
|
||||
|
||||
There are a number of cases when an application would not consider this an error situation.
|
||||
For example, this filter doesn't know how the `AuthorizationFilter` will respond to a missing relying party.
|
||||
In some cases it may be allowable.
|
||||
|
||||
In other cases, you may want your `AuthenticationEntryPoint` to be invoked, which would happen if this filter were to allow the request to continue to the `AuthorizationFilter`.
|
||||
|
||||
To improve this filter's flexibility, in Spring Security 7 it will continue the filter chain when there is no relying party registration found instead of throwing an exception.
|
||||
|
||||
For many applications, the only notable change will be that your `authenticationEntryPoint` will be invoked if the relying party registration cannot be found.
|
||||
When you have only one asserting party, this means by default a new authentication request will be built and sent back to the asserting party, which may cause a "Too Many Redirects" loop.
|
||||
|
||||
To see if you are affected in this way, you can prepare for this change in 6 by setting the following property in `Saml2WebSsoAuthenticationFilter`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
.saml2Login((saml2) -> saml2
|
||||
.withObjectPostProcessor(new ObjectPostProcessor<Saml2WebSsoAuhenticaionFilter>() {
|
||||
@Override
|
||||
public Saml2WebSsoAuthenticationFilter postProcess(Saml2WebSsoAuthenticationFilter filter) {
|
||||
filter.setContinueChainWhenNoRelyingPartyRegistrationFound(true);
|
||||
return filter;
|
||||
}
|
||||
})
|
||||
)
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
http {
|
||||
saml2Login { }
|
||||
withObjectPostProcessor(
|
||||
object : ObjectPostProcessor<Saml2WebSsoAuhenticaionFilter?>() {
|
||||
override fun postProcess(filter: Saml2WebSsoAuthenticationFilter): Saml2WebSsoAuthenticationFilter {
|
||||
filter.setContinueChainWhenNoRelyingPartyRegistrationFound(true)
|
||||
return filter
|
||||
}
|
||||
})
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<b:bean id="saml2PostProcessor" class="org.example.MySaml2WebSsoAuthenticationFilterBeanPostProcessor"/>
|
||||
----
|
||||
======
|
||||
|
||||
== Validate Response After Validating Assertions
|
||||
|
||||
In Spring Security 6, the order of authenticating a `<saml2:Response>` is as follows:
|
||||
|
||||
1. Verify the Response Signature, if any
|
||||
2. Decrypt the Response
|
||||
3. Validate Response attributes, like Destination and Issuer
|
||||
4. For each assertion, verify the signature, decrypt, and then validate its fields
|
||||
5. Check to ensure that the response has at least one assertion with a name field
|
||||
|
||||
This ordering sometimes poses challenges since some response validation is being done in Step 3 and some in Step 5.
|
||||
Specifically, this poses a chellenge when an application doesn't have a name field and doesn't need it to be validated.
|
||||
|
||||
In Spring Security 7, this is simplified by moving response validation to after assertion validation and combining the two separate validation steps 3 and 5.
|
||||
When this is complete, response validation will no longer check for the existence of the `NameID` attribute and rely on ``ResponseAuthenticationConverter``s to do this.
|
||||
|
||||
This will add support ``ResponseAuthenticationConverter``s that don't use the `NameID` element in their `Authentication` instance and so don't need it validated.
|
||||
|
||||
To opt-in to this behavior in advance, use `OpenSaml5AuthenticationProvider#setValidateResponseAfterAssertions` to `true` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
|
||||
provider.setValidateResponseAfterAssertions(true);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val provider = OpenSaml5AuthenticationProvider()
|
||||
provider.setValidateResponseAfterAssertions(true)
|
||||
----
|
||||
======
|
||||
|
||||
This will change the authentication steps as follows:
|
||||
|
||||
1. Verify the Response Signature, if any
|
||||
2. Decrypt the Response
|
||||
3. For each assertion, verify the signature, decrypt, and then validate its fields
|
||||
4. Validate Response attributes, like Destination and Issuer
|
||||
|
||||
Note that if you have a custom response authentication converter, then you are now responsible to check if the `NameID` element exists in the event that you need it.
|
||||
|
||||
Alternatively to updating your response authentication converter, you can specify a custom `ResponseValidator` that adds back in the check for the `NameID` element as follows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
|
||||
provider.setValidateResponseAfterAssertions(true);
|
||||
ResponseValidator responseValidator = ResponseValidator.withDefaults((responseToken) -> {
|
||||
Response response = responseToken.getResponse();
|
||||
Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
|
||||
Saml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND,
|
||||
"Assertion [" + firstAssertion.getID() + "] is missing a subject");
|
||||
Saml2ResponseValidationResult failed = Saml2ResponseValidationResult.failure(error);
|
||||
if (assertion.getSubject() == null) {
|
||||
return failed;
|
||||
}
|
||||
if (assertion.getSubject().getNameID() == null) {
|
||||
return failed;
|
||||
}
|
||||
if (assertion.getSubject().getNameID().getValue() == null) {
|
||||
return failed;
|
||||
}
|
||||
return Saml2ResponseValidationResult.success();
|
||||
});
|
||||
provider.setResponseValidator(responseValidator);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val provider = OpenSaml5AuthenticationProvider()
|
||||
provider.setValidateResponseAfterAssertions(true)
|
||||
val responseValidator = ResponseValidator.withDefaults { responseToken: ResponseToken ->
|
||||
val response = responseToken.getResponse()
|
||||
val assertion = CollectionUtils.firstElement(response.getAssertions())
|
||||
val error = Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND,
|
||||
"Assertion [" + firstAssertion.getID() + "] is missing a subject")
|
||||
val failed = Saml2ResponseValidationResult.failure(error)
|
||||
if (assertion.getSubject() == null) {
|
||||
return@withDefaults failed
|
||||
}
|
||||
if (assertion.getSubject().getNameID() == null) {
|
||||
return@withDefaults failed
|
||||
}
|
||||
if (assertion.getSubject().getNameID().getValue() == null) {
|
||||
return@withDefaults failed
|
||||
}
|
||||
return@withDefaults Saml2ResponseValidationResult.success()
|
||||
}
|
||||
provider.setResponseValidator(responseValidator)
|
||||
----
|
||||
======
|
@ -1,523 +0,0 @@
|
||||
= Web Migrations
|
||||
|
||||
== Favor Relative URIs
|
||||
|
||||
When redirecting to a login endpoint, Spring Security has favored absolute URIs in the past.
|
||||
For example, if you set your login page like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
// ...
|
||||
.formLogin((form) -> form.loginPage("/my-login"))
|
||||
// ...
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
http {
|
||||
formLogin {
|
||||
loginPage = "/my-login"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
<http ...>
|
||||
<form-login login-page="/my-login"/>
|
||||
</http>
|
||||
----
|
||||
======
|
||||
|
||||
then when redirecting to `/my-login` Spring Security would use a `Location:` like the following:
|
||||
|
||||
[source]
|
||||
----
|
||||
302 Found
|
||||
// ...
|
||||
Location: https://myapp.example.org/my-login
|
||||
----
|
||||
|
||||
However, this is no longer necessary given that the RFC is was based on is now obsolete.
|
||||
|
||||
In Spring Security 7, this is changed to use a relative URI like so:
|
||||
|
||||
[source]
|
||||
----
|
||||
302 Found
|
||||
// ...
|
||||
Location: /my-login
|
||||
----
|
||||
|
||||
Most applications will not notice a difference.
|
||||
However, in the event that this change causes problems, you can switch back to the Spring Security 6 behavior by setting the `favorRelativeUrls` value:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint("/my-login");
|
||||
entryPoint.setFavorRelativeUris(false);
|
||||
http
|
||||
// ...
|
||||
.exceptionHandling((exceptions) -> exceptions.authenticaitonEntryPoint(entryPoint))
|
||||
// ...
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
LoginUrlAuthenticationEntryPoint entryPoint = LoginUrlAuthenticationEntryPoint("/my-login")
|
||||
entryPoint.setFavorRelativeUris(false)
|
||||
|
||||
http {
|
||||
exceptionHandling {
|
||||
authenticationEntryPoint = entryPoint
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http entry-point-ref="myEntryPoint">
|
||||
<!-- ... -->
|
||||
</http>
|
||||
|
||||
<b:bean id="myEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
|
||||
<b:property name="favorRelativeUris" value="true"/>
|
||||
</b:bean>
|
||||
----
|
||||
======
|
||||
|
||||
== PortResolver
|
||||
|
||||
Spring Security uses an API called `PortResolver` to provide a workaround for a bug in Internet Explorer.
|
||||
The workaround is no longer necessary and can cause users problems in some scenarios.
|
||||
For this reason, Spring Security 7 will remove the `PortResolver` interface.
|
||||
|
||||
To prepare for this change, users should expose the `PortResolver.NO_OP` as a Bean named `portResolver`.
|
||||
This ensures that the `PortResolver` implementation that is used is a no-op (e.g. does nothing) which simulates the removal of `PortResolver`.
|
||||
An example configuration can be found below:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
PortResolver portResolver() {
|
||||
return PortResolver.NO_OP;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
open fun portResolver(): PortResolver {
|
||||
return PortResolver.NO_OP
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
|
||||
<util:constant id="portResolver"
|
||||
static-field="org.springframework.security.web.PortResolver.NO_OP">
|
||||
----
|
||||
======
|
||||
|
||||
[[use-path-pattern]]
|
||||
== Use PathPatternRequestMatcher by Default
|
||||
|
||||
In Spring Security 7, `AntPathRequestMatcher` and `MvcRequestMatcher` are no longer supported and the Java DSL requires that all URIs be absolute (less any context root).
|
||||
At that time, Spring Security 7 will use `PathPatternRequestMatcher` by default.
|
||||
|
||||
To check how prepared you are for this change, you can publish this bean:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun requestMatcherBuilder(): PathPatternRequestMatcherBuilderFactoryBean {
|
||||
return PathPatternRequestMatcherBuilderFactoryBean()
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<b:bean class="org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean"/>
|
||||
----
|
||||
======
|
||||
|
||||
This will tell the Spring Security DSL to use `PathPatternRequestMatcher` for all request matchers that it constructs.
|
||||
|
||||
In the event that you are directly constructing an object (as opposed to having the DSL construct it) that has a `setRequestMatcher` method. you should also proactively specify a `PathPatternRequestMatcher` there as well.
|
||||
|
||||
=== Migrate `exitUserUrl` and `switchUserUrl` Request Matchers in `SwitchUserFilter`
|
||||
|
||||
`SwitchUserFilter`, constructs an `AntPathRequestMatcher` in its `setExitUserUrl` and `setSwitchUserUrl` methods.
|
||||
This will change to use `PathPatternRequestMatcher` in Spring Security 7.
|
||||
|
||||
To prepare for this change, call `setExitUserMatcher` and `setSwithcUserMatcher` to provide this `PathPatternRequestMatcher` in advance.
|
||||
That is, change this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
SwitchUserFilter switchUser = new SwitchUserFilter();
|
||||
// ... other configuration
|
||||
switchUser.setExitUserUrl("/exit/impersonate");
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val switchUser = SwitchUserFilter()
|
||||
// ... other configuration
|
||||
switchUser.setExitUserUrl("/exit/impersonate")
|
||||
----
|
||||
======
|
||||
|
||||
to this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
SwitchUserFilter switchUser = new SwitchUserFilter();
|
||||
// ... other configuration
|
||||
switchUser.setExitUserMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, "/exit/impersonate"));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val switchUser = SwitchUserFilter()
|
||||
// ... other configuration
|
||||
switchUser.setExitUserMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, "/exit/impersonate"))
|
||||
----
|
||||
======
|
||||
|
||||
=== Migrate `filterProcessingUrl` Request Matcher in `AbstractAuthenticationProcessingFilter` Implementations
|
||||
|
||||
Spring Security 6 converts any processing endpoint configured through `setFilterProcessingUrl` to an `AntPathRequestMatcher`.
|
||||
In Spring Security 7, this will change to `PathPatternRequestMatcher`.
|
||||
|
||||
If you are directly invoking `setFilterProcessingUrl` on a filter that extends `AbstractAuthenticationProcessingFilter`, like `UsernamePasswordAuthenticationFilter`, `OAuth2LoginAuthenticationFilter`, `Saml2WebSsoAuthenticationFilter`, `OneTimeTokenAuthenticationFilter`, or `WebAuthnAuthenticationFilter`, call `setRequiredAuthenticationRequestMatcher` instead to provide this `PathPatternRequestMatcher` in advance.
|
||||
|
||||
That is, change this:
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
UsernamePasswordAuthenticationFilter usernamePassword = new UsernamePasswordAuthenticationFilter(authenticationManager);
|
||||
usernamePassword.setFilterProcessingUrl("/my/processing/url");
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val usernamePassword = UsernamePasswordAuthenticationFilter(authenticationManager)
|
||||
usernamePassword.setFilterProcessingUrl("/my/processing/url")
|
||||
----
|
||||
======
|
||||
|
||||
to this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
UsernamePasswordAuthenticationFilter usernamePassword = new UsernamePasswordAuthenticationFilter(authenticationManager);
|
||||
RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults().matcher("/my/processing/url");
|
||||
usernamePassword.setRequest(requestMatcher);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val usernamePassword = UsernamePasswordAuthenticationFilter(authenticationManager)
|
||||
val requestMatcher = PathPatternRequestMatcher.withDefaults().matcher("/my/processing/url")
|
||||
usernamePassword.setRequest(requestMatcher)
|
||||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
-----
|
||||
Most applications use the DSL instead of setting the `filterProcessingUrl` directly on a filter instance.
|
||||
-----
|
||||
|
||||
=== Migrate CAS Proxy Receptor Request Matcher
|
||||
|
||||
Spring Security 6 converts any configured `proxyReceptorUrl` to a request matcher that matches the end of the request, that is `/**/proxy/receptor`.
|
||||
In Spring Security 7, this pattern is not allowed and will change to using `PathPatternRequestMatcher`.
|
||||
Also in Spring Security 7m the URL should by absolute, excluding any context path, like so: `/proxy/receptor`.
|
||||
|
||||
So to prepare for these change, you can use `setProxyReceptorRequestMatcher` instead of `setProxyReceptorUrl`.
|
||||
|
||||
That is, change this:
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
casAuthentication.setProxyReceptorUrl("/proxy/receptor");
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
casAuthentication.setProxyReceptorUrl("/proxy/receptor")
|
||||
----
|
||||
======
|
||||
|
||||
to this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
casAuthentication.setProxyReceptorUrl(PathPatternRequestMatcher.withDefaults().matcher("/proxy/receptor"));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
casAuthentication.setProxyReceptorUrl(PathPatternRequestMatcher.withDefaults().matcher("/proxy/receptor"))
|
||||
----
|
||||
======
|
||||
|
||||
=== Migrate your WebInvocationPrivilegeEvaluator
|
||||
|
||||
If you are using Spring Security's JSP Taglibs or are using `WebInvocationPrivilegeEvaluator` directly, be aware of the following changes:
|
||||
|
||||
1. `RequestMatcherWebInvocationPrivilegeEvaluator` is deprecated in favor of `AuthorizationManagerWebInvocationPrivilegeEvaluator`
|
||||
2. `HandlerMappingIntrospectorRequestTransformer` is deprecated in favor of `PathPatternRequestTransformer`
|
||||
|
||||
If you are not constructing these directly, you can opt-in to both changes in advance by publishing a `PathPatternRequestTransformer` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
HttpServletRequestTransformer pathPatternRequestTransformer() {
|
||||
return new PathPatternRequestTransformer();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun pathPatternRequestTransformer(): HttpServletRequestTransformer {
|
||||
return PathPatternRequestTransformer()
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<b:bean class="org.springframework.security.web.access.PathPatternRequestTransformer"/>
|
||||
----
|
||||
======
|
||||
|
||||
Spring Security will take this as a signal to use the new implementations.
|
||||
|
||||
[[NOTE]]
|
||||
----
|
||||
One difference you may notice is that `AuthorizationManagerWebPrivilegeInvocationEvaluator` allows the authentication to be `null` if the authorization rule is `permitAll`.
|
||||
|
||||
Test your endpoints that `permitAll` in case JSP requests using this same require should not, in fact, be permitted.
|
||||
----
|
||||
|
||||
== Include the Servlet Path Prefix in Authorization Rules
|
||||
|
||||
For many applications <<use-path-pattern, the above>> will make no difference since most commonly all URIs listed are matched by the default servlet.
|
||||
|
||||
However, if you have other servlets with servlet path prefixes, xref:servlet/authorization/authorize-http-requests.adoc[then these paths now need to be supplied separately].
|
||||
|
||||
For example, if I have a Spring MVC controller with `@RequestMapping("/orders")` and my MVC application is deployed to `/mvc` (instead of the default servlet), then the URI for this endpoint is `/mvc/orders`.
|
||||
Historically, the Java DSL hasn't had a simple way to specify the servlet path prefix and Spring Security attempted to infer it.
|
||||
|
||||
Over time, we learned that these inference would surprise developers.
|
||||
Instead of taking this responsibility away from developers, now it is simpler to specify the servlet path prefix like so:
|
||||
|
||||
[method,java]
|
||||
----
|
||||
PathPatternRequestParser.Builder servlet = PathPatternRequestParser.withDefaults().basePath("/mvc");
|
||||
http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.requestMatchers(servlet.pattern("/orders/**").matcher()).authenticated()
|
||||
)
|
||||
----
|
||||
|
||||
|
||||
For paths that belong to the default servlet, use `PathPatternRequestParser.withDefaults()` instead:
|
||||
|
||||
[method,java]
|
||||
----
|
||||
PathPatternRequestParser.Builder request = PathPatternRequestParser.withDefaults();
|
||||
http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.requestMatchers(request.pattern("/js/**").matcher()).authenticated()
|
||||
)
|
||||
----
|
||||
|
||||
Note that this doesn't address every kind of servlet since not all servlets have a path prefix.
|
||||
For example, expressions that match the JSP Servlet might use an ant pattern `/**/*.jsp`.
|
||||
|
||||
There is not yet a general-purpose replacement for these, and so you are encouraged to use `RegexRequestMatcher`, like so: `regexMatcher("\\.jsp$")`.
|
||||
|
||||
For many applications this will make no difference since most commonly all URIs listed are matched by the default servlet.
|
||||
|
||||
[[use-redirect-to-https]]
|
||||
== Use RedirectToHttps Instead of Channel Security
|
||||
|
||||
Years ago, HTTPS at large was enough of a performance and configuration concern that applications wanted to be able to decide which segments of an application would require HTTPS.
|
||||
|
||||
`requires-channel` in XML and `requiresChannel` in Java Config allowed configurating an application with that in mind:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
.requiresChannel((channel) -> channel
|
||||
.requestMatchers("/secure/**").requiresSecureChannel()
|
||||
.requestMatchers("/insecure/**").requiresInsecureChannel()
|
||||
)
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
http {
|
||||
requiresChannel {
|
||||
secure("/secure/**")
|
||||
seccure("/insecure/**", "REQUIRES_INSECURE_CHANNEL")
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http>
|
||||
<intercept-url pattern="/secure/**" access="authenticated" requires-channel="REQUIRES_SECURE_CHANNEL"/>
|
||||
<intercept-url pattern="/insecure/**" access="authenticated" requires-channel="REQUIRES_INSECURE_CHANNEL"/>
|
||||
</http>
|
||||
----
|
||||
======
|
||||
|
||||
Modern applications should either always require HTTPS.
|
||||
However, there are times, like when developing locally, when one would like the application to use HTTP.
|
||||
Or, you may have continuing circumstances that require part of your application to be HTTP.
|
||||
|
||||
In any case, you can migrate to `redirect-to-https-request-matcher-ref` and `redirectToHttps` by first constructing a `RequestMatcher` that contains all circumstances where redirecting to HTTPS is needed.
|
||||
Then you can reference that request matcher like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
.redirectToHttps((https) -> https.requestMatchers("/secure/**"))
|
||||
// ...
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
var secure: RequestMatcher = PathPatternRequestMatcher.withDefaults().pattern("/secure/**")
|
||||
http {
|
||||
redirectToHttps {
|
||||
requestMatchers = secure
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<b:bean id="builder" class="org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher$Builder"/>
|
||||
<b:bean id="secure" class="org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher" factory-bean="builder" factory-method="matcher">
|
||||
<b:constructor-arg value="/secure/**"/>
|
||||
</b:bean>
|
||||
<http redirect-to-https-request-matcher-ref="secure">
|
||||
<intercept-url pattern="/secure/**" access="authenticated"/>
|
||||
<intercept-url pattern="/insecure/**" access="authenticated"/>
|
||||
<!-- ... -->
|
||||
</http>
|
||||
----
|
||||
======
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
If you have several circumstances where HTTP is needed, consider using `OrRequestMatcher` to combine them into a single `RequestMatcher` instance.
|
||||
=====
|
@ -1,9 +1,9 @@
|
||||
[[preparing]]
|
||||
= Preparing for 7.0
|
||||
= Preparing for 8.0
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
While Spring Security 7.0 does not have a release date yet, it is important to start preparing for it now.
|
||||
While Spring Security 8.0 does not have a release date yet, it is important to start preparing for it now.
|
||||
|
||||
This preparation guide is designed to summarize the biggest changes in Spring Security 7.0 and provide steps to prepare for them.
|
||||
This preparation guide is designed to summarize the biggest changes in Spring Security 8.0 and provide steps to prepare for them.
|
||||
|
||||
It is important to keep your application up to date with the latest Spring Security 6 and Spring Boot 3 releases.
|
||||
It is important to keep your application up to date with the latest Spring Security 7 and Spring Boot 4 releases.
|
@ -1,34 +1,20 @@
|
||||
[[migration]]
|
||||
= Migrating to 6.0
|
||||
= Migrating to 7.0
|
||||
:spring-security-reference-base-url: https://docs.spring.io/spring-security/reference
|
||||
|
||||
The Spring Security team has prepared the 5.8 release to simplify upgrading to Spring Security 6.0.
|
||||
Use 5.8 and
|
||||
ifdef::spring-security-version[]
|
||||
{spring-security-reference-base-url}/5.8/migration/index.html[its preparation steps]
|
||||
endif::[]
|
||||
ifndef::spring-security-version[]
|
||||
its preparation steps
|
||||
endif::[]
|
||||
to simplify updating to 6.0.
|
||||
Spring Security 6.5 is the last release in the 6.x generation of Spring Security.
|
||||
It provides strategies for configuring breaking changes to use the 7.0 way before updating.
|
||||
We recommend you use 6.5 and {spring-security-reference-base-url}/6.5/migration-7/index.html[its preparation steps] to simplify updating to 7.0.
|
||||
|
||||
After updating to 5.8, follow this guide to perform any remaining migration or cleanup steps.
|
||||
After updating to 6.5, follow this guide to perform any remaining migration or cleanup steps.
|
||||
|
||||
And recall that if you run into trouble, the preparation guide includes opt-out steps to revert to 5.x behaviors.
|
||||
|
||||
== Update to Spring Security 6
|
||||
== Update to Spring Security 7
|
||||
|
||||
The first step is to ensure you are the latest patch release of Spring Boot 3.0.
|
||||
Next, you should ensure you are on the latest patch release of Spring Security 6.
|
||||
For directions, on how to update to Spring Security 6 visit the xref:getting-spring-security.adoc[] section of the reference guide.
|
||||
|
||||
== Update Package Names
|
||||
|
||||
Now that you are updated, you need to change your `javax` imports to `jakarta` imports.
|
||||
|
||||
== Compile With `--parameters`
|
||||
|
||||
If you are using method parameter names in `@PreAuthorize`, `@PostAuthorize`, or any other method security annotations, you may need to xref:migration/servlet/authorization.adoc#compile-with-parameters[compile with `-parameters`].
|
||||
The first step is to ensure you are the latest patch release of Spring Boot 4.0.
|
||||
Next, you should ensure you are on the latest patch release of Spring Security 7.
|
||||
For directions, on how to update to Spring Security 7 visit the xref:getting-spring-security.adoc[] section of the reference guide.
|
||||
|
||||
== Perform Application-Specific Steps
|
||||
|
||||
|
@ -1,100 +1,3 @@
|
||||
= Reactive
|
||||
|
||||
If you have already performed the xref:migration/index.adoc[initial migration steps] for your Reactive application, you're now ready to perform steps specific to Reactive applications.
|
||||
|
||||
== Use `AuthorizationManager` for Method Security
|
||||
|
||||
In 6.0, `@EnableReactiveMethodSecurity` defaults `useAuthorizationManager` to `true`.
|
||||
So, to complete migration, {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.html[`@EnableReactiveMethodSecurity`] remove the `useAuthorizationManager` attribute:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
|
||||
----
|
||||
======
|
||||
|
||||
changes to:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@EnableReactiveMethodSecurity
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@EnableReactiveMethodSecurity
|
||||
----
|
||||
======
|
||||
|
||||
== Propagate ``AuthenticationServiceException``s
|
||||
|
||||
{security-api-url}org/springframework/security/web/server/authentication/AuthenticationWebFilter.html[`AuthenticationWebFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/web/server/ServerAuthenticationEntryPoint.html[`ServerAuthenticationEntryPoint`].
|
||||
Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container.
|
||||
|
||||
So, if you opted into this behavior by setting `rethrowAuthenticationServiceException` too `true`, you can now remove it like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
|
||||
bearerFailureHandler.setRethrowAuthenticationServiceException(true);
|
||||
AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
|
||||
basicFailureHandler.setRethrowAuthenticationServiceException(true);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
|
||||
bearerFailureHandler.setRethrowAuthenticationServiceException(true)
|
||||
val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
|
||||
basicFailureHandler.setRethrowAuthenticationServiceException(true)
|
||||
----
|
||||
======
|
||||
|
||||
changes to:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
|
||||
AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
|
||||
val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
|
||||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you configured the `ServerAuthenticationFailureHandler` only for the purpose of updating to 6.0, you can remove it completely.
|
||||
====
|
||||
|
@ -1,187 +0,0 @@
|
||||
= Authentication Migrations
|
||||
|
||||
The following steps relate to how to finish migrating authentication support.
|
||||
|
||||
== Propagate ``AuthenticationServiceException``s
|
||||
|
||||
{security-api-url}org/springframework/security/web/authentication/AuthenticationFilter.html[`AuthenticationFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`].
|
||||
Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container.
|
||||
|
||||
So, if you opted into this behavior by setting `rethrowAuthenticationServiceException` to `true`, you can now remove it like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
|
||||
AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
|
||||
handler.setRethrowAuthenticationServiceException(true);
|
||||
authenticationFilter.setAuthenticationFailureHandler(handler);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val authenticationFilter: AuthenticationFilter = AuthenticationFilter(...)
|
||||
val handler: AuthenticationEntryPointFailureHandler = AuthenticationEntryPointFailureHandler(...)
|
||||
handler.setRethrowAuthenticationServiceException(true)
|
||||
authenticationFilter.setAuthenticationFailureHandler(handler)
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
|
||||
<!-- ... -->
|
||||
<property ref="authenticationFailureHandler"/>
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
|
||||
<property name="rethrowAuthenticationServiceException" value="true"/>
|
||||
</bean>
|
||||
----
|
||||
======
|
||||
|
||||
changes to:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
|
||||
AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
|
||||
authenticationFilter.setAuthenticationFailureHandler(handler);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val authenticationFilter: AuthenticationFilter = AuthenticationFilter(...)
|
||||
val handler: AuthenticationEntryPointFailureHandler = AuthenticationEntryPointFailureHandler(...)
|
||||
authenticationFilter.setAuthenticationFailureHandler(handler)
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
|
||||
<!-- ... -->
|
||||
<property ref="authenticationFailureHandler"/>
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
|
||||
<!-- ... -->
|
||||
</bean>
|
||||
----
|
||||
======
|
||||
|
||||
[[servlet-opt-in-sha256-rememberme]]
|
||||
== Use SHA-256 in Remember Me
|
||||
|
||||
In 6.0, the `TokenBasedRememberMeServices` uses SHA-256 to encode and match the token.
|
||||
To complete the migration, any default values can be removed.
|
||||
|
||||
For example, if you opted in to the 6.0 default for `encodingAlgorithm` and `matchingAlgorithm` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.rememberMe((remember) -> remember
|
||||
.rememberMeServices(rememberMeServices)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
@Bean
|
||||
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
|
||||
RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
|
||||
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
|
||||
rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
|
||||
return rememberMe;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
XML::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http>
|
||||
<remember-me services-ref="rememberMeServices"/>
|
||||
</http>
|
||||
<bean id="rememberMeServices" class=
|
||||
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
|
||||
<property name="userDetailsService" ref="myUserDetailsService"/>
|
||||
<property name="key" value="springRocks"/>
|
||||
<property name="matchingAlgorithm" value="SHA256"/>
|
||||
<property name="encodingAlgorithm" value="SHA256"/>
|
||||
</bean>
|
||||
----
|
||||
======
|
||||
|
||||
then the defaults can be removed:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.rememberMe((remember) -> remember
|
||||
.rememberMeServices(rememberMeServices)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
@Bean
|
||||
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
|
||||
return new TokenBasedRememberMeServices(myKey, userDetailsService);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
XML::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http>
|
||||
<remember-me services-ref="rememberMeServices"/>
|
||||
</http>
|
||||
<bean id="rememberMeServices" class=
|
||||
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
|
||||
<property name="userDetailsService" ref="myUserDetailsService"/>
|
||||
<property name="key" value="springRocks"/>
|
||||
</bean>
|
||||
----
|
||||
======
|
||||
|
||||
== Default authorities for oauth2Login()
|
||||
|
||||
In Spring Security 5, the default `GrantedAuthority` given to a user that authenticates with an OAuth2 or OpenID Connect 1.0 provider (via `oauth2Login()`) is `ROLE_USER`.
|
||||
|
||||
In Spring Security 6, the default authority given to a user authenticating with an OAuth2 provider is `OAUTH2_USER`.
|
||||
The default authority given to a user authenticating with an OpenID Connect 1.0 provider is `OIDC_USER`.
|
||||
If you configured the `GrantedAuthoritiesMapper` only for the purpose of updating to 6.0, you can remove it completely.
|
@ -1,136 +0,0 @@
|
||||
= Authorization Migrations
|
||||
|
||||
The following steps relate to how to finish migrating authorization support.
|
||||
|
||||
== Use `AuthorizationManager` for Method Security
|
||||
|
||||
There are no further migration steps for this feature.
|
||||
|
||||
== Use `AuthorizationManager` for Message Security
|
||||
|
||||
In 6.0, `<websocket-message-broker>` defaults `use-authorization-manager` to `true`.
|
||||
So, to complete migration, remove any `websocket-message-broker@use-authorization-manager=true` attribute.
|
||||
|
||||
For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="primary"]
|
||||
----
|
||||
<websocket-message-broker use-authorization-manager="true"/>
|
||||
----
|
||||
======
|
||||
|
||||
changes to:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="primary"]
|
||||
----
|
||||
<websocket-message-broker/>
|
||||
----
|
||||
======
|
||||
|
||||
There are no further migrations steps for Java or Kotlin for this feature.
|
||||
|
||||
== Use `AuthorizationManager` for Request Security
|
||||
|
||||
In 6.0, `<http>` defaults `once-per-request` to `false`, `filter-all-dispatcher-types` to `true`, and `use-authorization-manager` to `true`.
|
||||
Also, xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests#filterAllDispatcherTypes`] defaults to `true`.
|
||||
So, to complete migration, any defaults values can be removed.
|
||||
|
||||
For example, if you opted in to the 6.0 default for `filter-all-dispatcher-types` or `authorizeHttpRequests#filterAllDispatcherTypes` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.filterAllDispatcherTypes(true)
|
||||
// ...
|
||||
)
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,java,role="secondary"]
|
||||
----
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
filterAllDispatcherTypes = true
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http use-authorization-manager="true" filter-all-dispatcher-types="true"/>
|
||||
----
|
||||
======
|
||||
|
||||
then the defaults may be removed:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
// ...
|
||||
)
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,java,role="secondary"]
|
||||
----
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http/>
|
||||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
`once-per-request` applies only when `use-authorization-manager="false"` and `filter-all-dispatcher-types` only applies when `use-authorization-manager="true"`
|
||||
====
|
||||
|
||||
[[compile-with-parameters]]
|
||||
=== Compile With `-parameters`
|
||||
|
||||
Spring Framework 6.1 https://github.com/spring-projects/spring-framework/issues/29559[removes LocalVariableTableParameterNameDiscoverer].
|
||||
This affects how `@PreAuthorize` and other xref:servlet/authorization/method-security.adoc[method security] annotations will process parameter names.
|
||||
If you are using method security annotations with parameter names, for example:
|
||||
|
||||
[source,java]
|
||||
.Method security annotation using `id` parameter name
|
||||
----
|
||||
@PreAuthorize("@authz.checkPermission(#id, authentication)")
|
||||
public void doSomething(Long id) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
You must compile with `-parameters` to ensure that the parameter names are available at runtime.
|
||||
For more information about this, please visit the https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#core-container[Upgrading to Spring Framework 6.1 page].
|
@ -1,44 +0,0 @@
|
||||
= Exploit Protection Migrations
|
||||
:spring-security-reference-base-url: https://docs.spring.io/spring-security/reference
|
||||
|
||||
The 5.8 migration guide contains several steps for
|
||||
ifdef::spring-security-version[]
|
||||
{spring-security-reference-base-url}/5.8/migration/servlet/exploits.html[exploit protection migrations] when updating to 6.0.
|
||||
endif::[]
|
||||
ifndef::spring-security-version[]
|
||||
exploit protection migrations when updating to 6.0.
|
||||
endif::[]
|
||||
You are encouraged to follow those steps first.
|
||||
|
||||
The following steps relate to how to finish migrating exploit protection support.
|
||||
|
||||
== Defer Loading CsrfToken
|
||||
|
||||
In Spring Security 5.8, the default `CsrfTokenRequestHandler` for making the `CsrfToken` available to the application is `CsrfTokenRequestAttributeHandler`.
|
||||
The default for the field `csrfRequestAttributeName` is `null`, which causes the CSRF token to be loaded on every request.
|
||||
|
||||
In Spring Security 6, `csrfRequestAttributeName` defaults to `_csrf`.
|
||||
If you configured the following only for the purpose of updating to 6.0, you can now remove it:
|
||||
|
||||
requestHandler.setCsrfRequestAttributeName("_csrf");
|
||||
|
||||
== Protect against CSRF BREACH
|
||||
|
||||
In Spring Security 5.8, the default `CsrfTokenRequestHandler` for making the `CsrfToken` available to the application is `CsrfTokenRequestAttributeHandler`.
|
||||
`XorCsrfTokenRequestAttributeHandler` was added to allow opting into CSRF BREACH support.
|
||||
|
||||
In Spring Security 6, `XorCsrfTokenRequestAttributeHandler` is the default `CsrfTokenRequestHandler` for making the `CsrfToken` available.
|
||||
If you configured the `XorCsrfTokenRequestAttributeHandler` only for the purpose of updating to 6.0, you can remove it completely.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you have set the `csrfRequestAttributeName` to `null` in order to opt out of deferred tokens, or if you have configured a `CsrfTokenRequestHandler` for any other reason, you can leave the configuration in place.
|
||||
====
|
||||
|
||||
== CSRF BREACH with WebSocket support
|
||||
|
||||
In Spring Security 5.8, the default `ChannelInterceptor` for making the `CsrfToken` available with xref:servlet/integrations/websocket.adoc[WebSocket Security] is `CsrfChannelInterceptor`.
|
||||
`XorCsrfChannelInterceptor` was added to allow opting into CSRF BREACH support.
|
||||
|
||||
In Spring Security 6, `XorCsrfChannelInterceptor` is the default `ChannelInterceptor` for making the `CsrfToken` available.
|
||||
If you configured the `XorCsrfChannelInterceptor` only for the purpose of updating to 6.0, you can remove it completely.
|
80
docs/modules/ROOT/pages/migration/servlet/oauth2.adoc
Normal file
80
docs/modules/ROOT/pages/migration/servlet/oauth2.adoc
Normal file
@ -0,0 +1,80 @@
|
||||
= OAuth 2.0 Migrations
|
||||
|
||||
== Validate `typ` Header with `JwtTypeValidator`
|
||||
|
||||
If when following the 6.5 preparatory steps you set `validateTypes` to `false`, you can now remove it.
|
||||
You can also remove explicitly adding `JwtTypeValidator` to the list of defaults.
|
||||
|
||||
For example, change this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
new JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
.validateTypes(false) <1>
|
||||
// ... your remaining configuration
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
|
||||
JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - Switch off Nimbus verifying the `typ`
|
||||
<2> - Add the default `typ` validator
|
||||
|
||||
to this:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
// ... your remaining configuration <1>
|
||||
.build();
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)); <2>
|
||||
return jwtDecoder;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun jwtDecoder(): JwtDecoder {
|
||||
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
|
||||
// ... your remaining configuration
|
||||
.build()
|
||||
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)) <2>
|
||||
return jwtDecoder
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> - `validateTypes` now defaults to `false`
|
||||
<2> - `JwtTypeValidator#jwt` is added by all `createDefaultXXX` methods
|
@ -1,49 +0,0 @@
|
||||
= Session Management Migrations
|
||||
|
||||
The following steps relate to how to finish migrating session management support.
|
||||
|
||||
== Require Explicit Saving of SecurityContextRepository
|
||||
|
||||
In Spring Security 5, the default behavior is for the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to automatically be saved to the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] using the xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`].
|
||||
Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
|
||||
Unfortunately, automatic persistence of the `SecurityContext` can surprise users when it is done prior to the request completing (i.e. just prior to committing the `HttpServletResponse`).
|
||||
It also is complex to keep track of the state to determine if a save is necessary causing unnecessary writes to the `SecurityContextRepository` (i.e. `HttpSession`) at times.
|
||||
|
||||
In Spring Security 6, the default behavior is that the xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[`SecurityContextHolderFilter`] will only read the `SecurityContext` from `SecurityContextRepository` and populate it in the `SecurityContextHolder`.
|
||||
Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
|
||||
This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Saving the context is also needed when clearing it out, for example during logout. Refer to this section to xref:servlet/authentication/session-management.adoc#properly-clearing-authentication[know more about that].
|
||||
====
|
||||
|
||||
If you are explicitly opting into Spring Security 6's new defaults, the following configuration can be removed to accept the Spring Security 6 defaults.
|
||||
|
||||
|
||||
include::partial$servlet/architecture/security-context-explicit.adoc[]
|
||||
|
||||
== Multiple SecurityContextRepository
|
||||
|
||||
In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] was `HttpSessionSecurityContextRepository`.
|
||||
|
||||
In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
|
||||
If you configured the `SecurityContextRepository` only for the purpose of updating to 6.0, you can remove it completely.
|
||||
|
||||
== Deprecation in SecurityContextRepository
|
||||
|
||||
There are no further migration steps for this deprecation.
|
||||
|
||||
[[requestcache-query-optimization]]
|
||||
== Optimize Querying of `RequestCache`
|
||||
|
||||
In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
|
||||
This means that in a typical setup, that in order to use the xref:servlet/architecture.adoc#requestcache[`RequestCache`] the `HttpSession` is queried on every request.
|
||||
|
||||
In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
|
||||
This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
|
||||
|
||||
In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
|
||||
If you are not overriding the defaults (i.e. using `NullRequestCache`), then the following configuration can be used to explicitly opt into the Spring Security 6 behavior in Spring Security 5.8:
|
||||
|
||||
include::partial$servlet/architecture/request-cache-continue.adoc[]
|
Loading…
x
Reference in New Issue
Block a user