val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
----
-====
+======
[[authentication-password-storage-dpe-format]]
=== Password Storage Format
@@ -130,12 +136,10 @@ val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, en
The general format for a password is:
.DelegatingPasswordEncoder Storage Format
-====
[source,text,attrs="-attributes"]
----
{id}encodedPassword
----
-====
Such that `id` is an identifier used to look up which `PasswordEncoder` should be used and `encodedPassword` is the original encoded password for the selected `PasswordEncoder`.
The `id` must be at the beginning of the password, start with `{` and end with `}`.
@@ -144,7 +148,6 @@ For example, the following might be a list of passwords encoded using different
All of the original passwords are "password".
.DelegatingPasswordEncoder Encoded Passwords Example
<1> The first password would have a `PasswordEncoder` id of `bcrypt` and encodedPassword of `$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG`.
When matching it would delegate to `BCryptPasswordEncoder`
@@ -182,12 +184,10 @@ In the `DelegatingPasswordEncoder` we constructed above, that means that the res
With the above configuration, when a password manager navigates to `/.well-known/change-password`, then Spring Security will redirect to `/update-password`.
@@ -245,7 +233,6 @@ However, you must be very careful as there are CSRF exploits that can impact JSO
For example, a malicious user can create a http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html[CSRF with JSON using the following form]:
@@ -254,13 +241,11 @@ For example, a malicious user can create a http://blog.opensecurityresearch.com/
value="Win Money!"/>
</form>
----
-====
This will produce the following JSON structure
.CSRF with JSON request
-====
[source,javascript]
----
{ "amount": 100,
@@ -269,13 +254,11 @@ This will produce the following JSON structure
"ignore_me": "=test"
}
----
-====
If an application were not validating the Content-Type, then it would be exposed to this exploit.
Depending on the setup, a Spring MVC application that validates the Content-Type could still be exploited by updating the URL suffix to end with `.json` as shown below:
The optional `includeSubDomains` directive instructs the browser that subdomains (e.g. secure.mybank.example.com) should also be treated as an HSTS domain.
@@ -247,12 +239,10 @@ A security policy contains a set of security policy directives, each responsible
For example, a web application can declare that it expects to load scripts from specific, trusted sources, by including the following header in the response:
An attempt to load a script from another source other than what is declared in the `script-src` directive will be blocked by the user-agent.
Additionally, if the https://www.w3.org/TR/CSP2/#directive-report-uri[report-uri] directive is declared in the security policy, then the violation will be reported by the user-agent to the declared URL.
@@ -260,12 +250,10 @@ Additionally, if the https://www.w3.org/TR/CSP2/#directive-report-uri[report-uri
For example, if a web application violates the declared security policy, the following response header will instruct the user-agent to send violation reports to the URL specified in the policy's `report-uri` directive.
https://www.w3.org/TR/CSP2/#violation-reports[Violation reports] are standard JSON structures that can be captured either by the web application's own API or by a publicly hosted CSP violation reporting service, such as, https://report-uri.com/.
@@ -276,12 +264,10 @@ When a policy is deemed effective, it can be enforced by using the `Content-Secu
Given the following response header, the policy declares that scripts may be loaded from one of two possible sources.
If the site violates this policy, by attempting to load a script from _evil.com_, the user-agent will send a violation report to the declared URL specified by the _report-uri_ directive, but still allow the violating resource to load nevertheless.
@@ -308,12 +294,10 @@ page the user was on.
Spring Security's approach is to use https://www.w3.org/TR/referrer-policy/[Referrer Policy] header, which provides different https://www.w3.org/TR/referrer-policy/#referrer-policies[policies]:
.Referrer Policy Example
-====
[source]
----
Referrer-Policy: same-origin
----
-====
The Referrer-Policy response header instructs the browser to let the destination knows the source where the user was previously.
@@ -328,12 +312,10 @@ Refer to the relevant sections to see how to configure both xref:servlet/exploit
https://wicg.github.io/feature-policy/[Feature Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.
.Feature Policy Example
-====
[source]
----
Feature-Policy: geolocation 'self'
----
-====
With Feature Policy, developers can opt-in to a set of "policies" for the browser to enforce on specific features used throughout your site.
These policies restrict what APIs the site can access or modify the browser's default behavior for certain features.
@@ -350,12 +332,10 @@ Refer to the relevant sections to see how to configure both xref:servlet/exploit
https://w3c.github.io/webappsec-permissions-policy/[Permissions Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.
.Permissions Policy Example
-====
[source]
----
Permissions-Policy: geolocation=(self)
----
-====
With Permissions Policy, developers can opt-in to a set of "policies" for the browser to enforce on specific features used throughout your site.
These policies restrict what APIs the site can access or modify the browser's default behavior for certain features.
val context: SecurityContext = SecurityContextHolder.createEmptyContext()
@@ -171,7 +183,7 @@ val originalRunnable = Runnable {
executor.execute(originalRunnable)
----
-====
+======
The code performs the following steps:
@@ -185,8 +197,10 @@ In this instance, the same `SecurityContext` will be used for every Runnable sub
This is nice if we are running background tasks that need to be run by a user with elevated privileges.
* At this point you may be asking yourself "How does this shield my code of any knowledge of Spring Security?" Instead of creating the `SecurityContext` and the `DelegatingSecurityContextExecutor` in our own code, we can inject an already initialized instance of `DelegatingSecurityContextExecutor`.
Now our code is unaware that the `SecurityContext` is being propagated to the `Thread`, then the `originalRunnable` is run, and then the `SecurityContextHolder` is cleared out.
In this example, the same user is being used to run each thread.
@@ -224,8 +239,10 @@ This can be done by removing the `SecurityContext` argument from our `Delegating
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
new DelegatingSecurityContextExecutor(delegateExecutor);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = DelegatingSecurityContextExecutor(delegateExecutor)
----
-====
+======
Now anytime `executor.execute(Runnable)` is executed the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`.
This means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.
@@ -239,15 +266,17 @@ val encoder = BCryptPasswordEncoder(16)
val result: String = encoder.encode("myPassword")
assertTrue(encoder.matches("myPassword", result))
----
-====
+======
The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords.
In order to defeat password cracking PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system.
.Pbkdf2PasswordEncoder
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
// Create an encoder with all the defaults
@@ -256,7 +285,8 @@ String result = encoder.encode("myPassword");
@@ -10,8 +10,10 @@ It is not only useful but necessary to include the user in the queries to suppor
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`.
In Java Configuration, this would look like:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -20,7 +22,8 @@ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -28,7 +31,7 @@ fun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {
return SecurityEvaluationContextExtension()
}
----
-====
+======
In XML Configuration, this would look like:
@@ -43,8 +46,10 @@ In XML Configuration, this would look like:
Now Spring Security can be used within your queries.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Repository
@@ -54,7 +59,8 @@ public interface MessageRepository extends PagingAndSortingRepository<Message,Lo
@@ -6,8 +6,10 @@ This can improve the performance of serializing Spring Security related classes
To use it, register the `SecurityJackson2Modules.getModules(ClassLoader)` with `ObjectMapper` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
ObjectMapper mapper = new ObjectMapper();
@@ -21,7 +23,8 @@ SecurityContext context = new SecurityContextImpl();
String json = mapper.writeValueAsString(context);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val mapper = ObjectMapper()
@@ -34,7 +37,7 @@ val context: SecurityContext = SecurityContextImpl()
// ...
val json: String = mapper.writeValueAsString(context)
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].
@@ -80,7 +74,6 @@ If you use additional features (such as LDAP, OpenID, and others), you need to a
When you use Spring Security without Spring Boot, the preferred way is to use Spring Security's BOM to ensure a consistent version of Spring Security is used throughout the entire project. The following example shows how to do so:
.pom.xml
-====
[source,xml,ubs="verbatim,attributes"]
----
<dependencyManagement>
@@ -96,12 +89,10 @@ When you use Spring Security without Spring Boot, the preferred way is to use Sp
</dependencies>
</dependencyManagement>
----
-====
A minimal Spring Security Maven set of dependencies typically looks like the following:
.pom.xml
-====
[source,xml,subs="verbatim,attributes"]
----
<dependencies>
@@ -116,7 +107,6 @@ A minimal Spring Security Maven set of dependencies typically looks like the fol
</dependency>
</dependencies>
----
-====
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].
@@ -125,7 +115,6 @@ Many users are likely to run afoul of the fact that Spring Security's transitive
The easiest way to resolve this is to use the `spring-framework-bom` within the `<dependencyManagement>` section of your `pom.xml` as the following example shows:
.pom.xml
-====
[source,xml,subs="verbatim,attributes"]
----
<dependencyManagement>
@@ -141,7 +130,6 @@ The easiest way to resolve this is to use the `spring-framework-bom` within the
</dependencies>
</dependencyManagement>
----
-====
The preceding example ensures that all the transitive dependencies of Spring Security use the Spring {spring-core-version} modules.
@@ -155,7 +143,6 @@ All GA releases (that is, versions ending in .RELEASE) are deployed to Maven Cen
If you use a SNAPSHOT version, you need to ensure that you have the Spring Snapshot repository defined, as the following example shows:
.pom.xml
-====
[source,xml]
----
<repositories>
@@ -167,12 +154,10 @@ If you use a SNAPSHOT version, you need to ensure that you have the Spring Snaps
</repository>
</repositories>
----
-====
If you use a milestone or release candidate version, you need to ensure that you have the Spring Milestone repository defined, as the following example shows:
.pom.xml
-====
[source,xml]
----
<repositories>
@@ -184,7 +169,6 @@ If you use a milestone or release candidate version, you need to ensure that you
</repository>
</repositories>
----
-====
[[getting-gradle]]
== Gradle
@@ -201,7 +185,6 @@ The simplest and preferred method to use the starter is to use https://docs.spri
Alternatively, you can manually add the starter, as the following example shows:
Since Spring Security makes breaking changes only in major releases, it is safe to use a newer version of Spring Security with Spring Boot.
However, at times, you may need to update the version of Spring Framework as well.
You can do so by adding a Gradle property, as the following example shows:
.build.gradle
-====
[source,groovy]
[subs="verbatim,attributes"]
----
ext['spring.version']='{spring-core-version}'
----
-====
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].
@@ -244,7 +222,6 @@ When you use Spring Security without Spring Boot, the preferred way is to use Sp
You can do so by using the https://github.com/spring-gradle-plugins/dependency-management-plugin[Dependency Management Plugin], as the following example shows:
.build.gradle
-====
[source,groovy]
[subs="verbatim,attributes"]
----
@@ -258,12 +235,10 @@ dependencyManagement {
}
}
----
-====
A minimal Spring Security Maven set of dependencies typically looks like the following:
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].
@@ -282,7 +256,6 @@ The easiest way to resolve this is to use the `spring-framework-bom` within your
You can do so by using the https://github.com/spring-gradle-plugins/dependency-management-plugin[Dependency Management Plugin], as the following example shows:
.build.gradle
-====
[source,groovy]
[subs="verbatim,attributes"]
----
@@ -296,7 +269,6 @@ dependencyManagement {
}
}
----
-====
The preceding example ensures that all the transitive dependencies of Spring Security use the Spring {spring-core-version} modules.
@@ -305,35 +277,29 @@ The preceding example ensures that all the transitive dependencies of Spring Sec
All GA releases (that is, versions ending in .RELEASE) are deployed to Maven Central, so using the mavenCentral() repository is sufficient for GA releases. The following example shows how to do so:
.build.gradle
-====
[source,groovy]
----
repositories {
mavenCentral()
}
----
-====
If you use a SNAPSHOT version, you need to ensure you have the Spring Snapshot repository defined, as the following example shows:
.build.gradle
-====
[source,groovy]
----
repositories {
maven { url 'https://repo.spring.io/snapshot' }
}
----
-====
If you use a milestone or release candidate version, you need to ensure that you have the Spring Milestone repository defined, as the following example shows:
@@ -124,15 +136,17 @@ fun passwordEncoder(): PasswordEncoder {
return current
}
----
-====
+======
==== Use `DelegatingPasswordEncoder`
Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatingPasswordEncoder`.
The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatingPasswordEncoder`.
The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
Once you are not using the deprecated constructor, the next step is to prepare your code to upgrade to the latest standards by using `DelegatingPasswordEncoder`.
The following code configures the delegating encoder to detect passwords that are using `current` and replace them with the latest:
@@ -117,7 +126,7 @@ open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChai
}
}
----
-====
+======
[[reactive-csrf-breach-opt-out]]
=== Opt-out Steps
@@ -131,8 +140,10 @@ If you are using AngularJS and the https://angular.io/api/common/http/HttpClient
In this case, you can configure Spring Security to validate the raw `CsrfToken` from the cookie while keeping CSRF BREACH protection of the response using a custom `ServerCsrfTokenRequestHandler` with delegation, like so:
.Configure `CsrfToken` BREACH Protection to validate raw tokens
At some point, you will want to fully migrate to Spring Security 6 defaults. But how do you know when it is safe to do so?
Let's suppose that you deployed your application using SHA-256 as the encoding algorithm (as you have done <<servlet-opt-in-sha256-sha256-encoding,here>>) on November 1st, if you have the value for the `tokenValiditySeconds` property set to N days (14 is the default), you can migrate to SHA-256 N days after November 1st (which is November 15th in this example).
By that time, all the tokens generated with MD5 will have expired.
.Use Spring Security 6 defaults, SHA-256 for both encoding and matching
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -97,7 +102,8 @@ public class SecurityConfig {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -112,13 +118,15 @@ public class SecurityConfig {
If you are having problems with the Spring Security 6 defaults, you can explicitly opt into 5.8 defaults using the following configuration:
.Use MD5 for both encoding and matching algorithms
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -146,7 +154,8 @@ public class SecurityConfig {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -161,7 +170,7 @@ public class SecurityConfig {
<property name="encodingAlgorithm" value="MD5"/>
</bean>
----
-====
+======
== Propagate ``AuthenticationServiceException``s
@@ -172,8 +181,10 @@ Because ``AuthenticationServiceException``s represent a server-side error instea
To prepare for the 6.0 default, wire `AuthenticationFilter` instances with a `AuthenticationFailureHandler` that rethrows ``AuthenticationServiceException``s, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
@@ -10,8 +10,10 @@ In 6.0, `@Configuration` is removed from `@EnableWebSecurity`, `@EnableMethodSec
To prepare for this, wherever you are using one of these annotations, you may need to add `@Configuration`.
For example, `@EnableMethodSecurity` changes from:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableMethodSecurity
@@ -19,8 +21,12 @@ public class MyConfiguration {
// ...
}
----
+======
-.Kotlin
+[tabs]
+======
+Kotlin::
++
[source,java,role="primary"]
----
@EnableMethodSecurity
@@ -28,12 +34,14 @@ open class MyConfiguration {
// ...
}
----
-====
+======
to:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -42,8 +50,12 @@ public class MyConfiguration {
// ...
}
----
+======
-.Kotlin
+[tabs]
+======
+Kotlin::
++
[source,java,role="primary"]
----
@Configuration
@@ -52,7 +64,7 @@ open class MyConfiguration {
// ...
}
----
-====
+======
[[use-new-requestmatchers]]
== Use the new `requestMatchers` methods
@@ -67,8 +79,10 @@ In summary, the new methods choose the `MvcRequestMatcher` implementation if you
To start using the new methods, you can replace the deprecated methods with the new ones. For example, the following application configuration:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -88,12 +102,14 @@ public class SecurityConfig {
}
----
-====
+======
can be changed to:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -113,13 +129,15 @@ public class SecurityConfig {
}
----
-====
+======
If you have Spring MVC in the classpath and are using the `mvcMatchers` methods, you can replace it with the new methods and Spring Security will choose the `MvcRequestMatcher` implementation for you.
The following configuration:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -139,12 +157,14 @@ public class SecurityConfig {
}
----
-====
+======
is equivalent to:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -164,12 +184,14 @@ public class SecurityConfig {
}
----
-====
+======
If you are customizing the `servletPath` property of the `MvcRequestMatcher`, you can now use the `MvcRequestMatcher.Builder` to create `MvcRequestMatcher` instances that share the same servlet path:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -190,12 +212,14 @@ public class SecurityConfig {
}
----
-====
+======
The code above can be rewritten using the `MvcRequestMatcher.Builder` and the `requestMatchers` method:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -217,13 +241,15 @@ public class SecurityConfig {
}
----
-====
+======
If you are having problem with the new `requestMatchers` methods, you can always switch back to the `RequestMatcher` implementation that you were using.
For example, if you still want to use `AntPathRequestMatcher` and `RegexRequestMatcher` implementations, you can use the `requestMatchers` method that accepts a `RequestMatcher` instance:
@@ -247,14 +273,16 @@ public class SecurityConfig {
}
----
-====
+======
Note that the above sample uses static factory methods from {security-api-url}org/springframework/security/web/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] and {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to improve readability.
If you are using the `WebSecurityCustomizer` interface, you can replace the deprecated `antMatchers` methods:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -262,12 +290,14 @@ public WebSecurityCustomizer webSecurityCustomizer() {
The same way, if you are customizing the CSRF configuration to ignore some paths, you can replace the deprecated methods with the `requestMatchers` methods:
If you are having problems with the `securityMatchers` methods choosing the `RequestMatcher` implementation for you, you can always choose the `RequestMatcher` implementation yourself:
@@ -481,8 +529,10 @@ Spring Security 5.4 introduced the capability to publish a `SecurityFilterChain`
In 6.0, `WebSecurityConfigurerAdapter` is removed.
To prepare for this change, you can replace constructs like:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -500,7 +550,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -519,12 +570,14 @@ open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
}
----
-====
+======
with:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -543,7 +596,8 @@ public class SecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -562,15 +616,17 @@ open class SecurityConfiguration {
}
----
-====
+======
=== Publish a `WebSecurityCustomizer` Bean
Spring Security 5.4 https://github.com/spring-projects/spring-security/issues/8978[introduced `WebSecurityCustomizer`] to replace `configure(WebSecurity web)` in `WebSecurityConfigurerAdapter`.
To prepare for its removal, you can replace code like the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -584,7 +640,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -596,12 +653,14 @@ open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
}
----
-====
+======
with:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -615,7 +674,8 @@ public class SecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -628,7 +688,7 @@ open class SecurityConfiguration {
}
----
-====
+======
=== Publish an `AuthenticationManager` Bean
@@ -639,8 +699,10 @@ Preparing for its removal will differ based on your reason for using it.
If you are using `auth.ldapAuthentication()` for xref:servlet/authentication/passwords/ldap.adoc[LDAP authentication support], you can replace:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -659,7 +721,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -676,12 +739,14 @@ open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
}
----
-====
+======
with:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -705,7 +770,8 @@ public class SecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -727,14 +793,16 @@ open class SecurityConfiguration {
}
}
----
-====
+======
==== JDBC Authentication
If you are using `auth.jdbcAuthentication()` for xref:servlet/authentication/passwords/jdbc.adoc[JDBC Authentication support], you can replace:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -761,7 +829,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -786,12 +855,14 @@ open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
}
}
----
-====
+======
with:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -818,7 +889,8 @@ public class SecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -844,14 +916,16 @@ open class SecurityConfiguration {
}
}
----
-====
+======
==== In-Memory Authentication
If you are using `auth.inMemoryAuthentication()` for xref:servlet/authentication/passwords/in-memory.adoc[In-Memory Authentication support], you can replace:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -869,7 +943,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -885,12 +960,14 @@ open class SecurityConfiguration: WebSecurityConfigurerAdapter() {
}
}
----
-====
+======
with:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -907,7 +984,8 @@ public class SecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -923,7 +1001,7 @@ open class SecurityConfiguration {
}
}
----
-====
+======
== Add `@Configuration` to `@Enable*` annotations
@@ -942,8 +1020,10 @@ The following annotations had their `@Configuration` removed:
For example, if you are using `@EnableWebSecurity`, you will need to change:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -951,12 +1031,14 @@ public class SecurityConfig {
// ...
}
----
-====
+======
to:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -965,7 +1047,7 @@ public class SecurityConfig {
// ...
}
----
-====
+======
And the same applies to every other annotation listed above.
@@ -92,8 +96,10 @@ In this case, you have several options for restoring the behavior your client-si
One option is to add a `Filter` that eagerly renders the `CsrfToken` to the response regardless of which request is made first, like so:
.Add a `Filter` to return a cookie on the response
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -128,7 +134,8 @@ private static final class CsrfCookieFilter extends OncePerRequestFilter {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -159,7 +166,7 @@ class CsrfCookieFilter : OncePerRequestFilter() {
}
----
-====
+======
The option above does not require changes to the single-page application, but does cause the `CsrfToken` to be loaded on every request.
If you do not wish to add a `Filter` to eagerly load tokens on every request, additional options are listed below.
@@ -170,8 +177,10 @@ If you are using sessions, your application will benefit from deferred tokens.
Instead of opting out, another option is to add a new `@RestController` with a `/csrf` endpoint, like so:
.Add a `/csrf` endpoint
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RestController
@@ -185,7 +194,8 @@ public class CsrfController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RestController
@@ -198,7 +208,7 @@ class CsrfController {
}
----
-====
+======
[NOTE]
====
@@ -225,8 +235,10 @@ If you simply wish to opt out of deferred tokens altogether, that option is list
If deferred tokens break your application for another reason, then you can explicitly opt into the 5.8 defaults using the following configuration:
.Explicit Configure `CsrfToken` with 5.8 Defaults
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -243,7 +255,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -260,7 +273,8 @@ open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -274,7 +288,7 @@ open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
</b:property>
</b:bean>
----
-====
+======
[NOTE]
====
@@ -287,8 +301,10 @@ This causes the `CsrfToken` to be loaded on every request.
If the steps for <<Defer Loading CsrfToken>> work for you, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
@@ -347,8 +365,10 @@ If you are using AngularJS and the https://angular.io/api/common/http/HttpClient
In this case, you can configure Spring Security to validate the raw `CsrfToken` from the cookie while keeping CSRF BREACH protection of the response using a custom `CsrfTokenRequestHandler` with delegation, like so:
.Configure `CsrfToken` BREACH Protection to validate raw tokens
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -371,7 +391,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -393,7 +414,8 @@ open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -405,7 +427,7 @@ open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
This is the RECOMMENDED way to configure Spring Security to work with a client-side application that uses cookie values, because it continues to allow the response to return a randomized value for the CSRF token in case the application returns HTML or other responses that could be vulnerable to BREACH without your knowledge.
@@ -431,8 +453,10 @@ If CSRF BREACH protection does not work for you for another reason, you can opt
If the steps for <<Protect against CSRF BREACH>> work for normal HTTP requests and you are using xref:servlet/integrations/websocket.adoc[WebSocket Security] support, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` with xref:servlet/integrations/websocket.adoc#websocket-sameorigin-csrf[Stomp headers].
@@ -109,8 +113,10 @@ private fun grantedAuthoritiesMapper(): GrantedAuthoritiesMapper {
If configuring the new authorities gives you trouble, you can opt out and explicitly use the 5.8 authority of `ROLE_USER` with the following configuration.
Also, since `setAuthnRequestCustomizer` has direct access to the `HttpServletRequest`, there is no need for a `Saml2AuthenticationRequestContextResolver`.
Simply use `setAuthnRequestCustomizer` to read directly from `HttpServletRequest` this information you need.
@@ -191,8 +204,10 @@ Simply use `setAuthnRequestCustomizer` to read directly from `HttpServletRequest
val assertingPartyEntityId: String = registration.getAssertingPartyDetails().getEntityId()
@@ -322,6 +351,6 @@ val singleSignOnServiceLocation: String = registration.getAssertingPartyDetails(
val entityId: String = registration.getEntityId()
val verifying: List<Saml2X509Credential> = registration.getAssertingPartyDetails().getVerificationX509Credentials()
----
-====
+======
For a complete listing of all changed methods, please see {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[``RelyingPartyRegistration``'s JavaDoc].
Similar to xref:servlet/authentication/x509.adoc#servlet-x509[Servlet X.509 authentication], reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.
Below is an example of a reactive x509 security configuration:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -19,7 +21,8 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -32,14 +35,16 @@ fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
-====
+======
In the configuration above, when neither `principalExtractor` nor `authenticationManager` is provided defaults will be used. The default principal extractor is `SubjectDnX509PrincipalExtractor` which extracts the CN (common name) field from a certificate provided by a client. The default authentication manager is `ReactivePreAuthenticatedAuthenticationManager` which performs user account validation, checking that user account with a name extracted by `principalExtractor` exists and it is not locked, disabled, or expired.
The next example demonstrates how these defaults can be overridden.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -66,7 +71,8 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -88,7 +94,7 @@ fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
}
}
----
-====
+======
In this example, a username is extracted from the OU field of a client certificate instead of CN, and account lookup using `ReactiveUserDetailsService` is not performed at all. Instead, if the provided certificate issued to an OU named "Trusted Org Unit", a request will be authenticated.
@@ -41,15 +43,17 @@ public class MethodSecurityConfig {
// ...
}
----
-====
+======
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
Spring Security's native annotation support defines a set of attributes for the method.
These will be passed to the various method interceptors, like `AuthorizationManagerBeforeReactiveMethodInterceptor`, for it to make the actual decision:
and it will be invoked after the `@PostAuthorize` interceptor.
@@ -291,8 +311,10 @@ When intercepting coroutines, only the first interceptor participates.
If any other interceptors are present and come after Spring Security's method security interceptor, https://github.com/spring-projects/spring-framework/issues/22462[they will be skipped].
====
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
fun findMessageByUsername(username: String): Mono<String> {
return Mono.just("Hi $username")
}
----
-====
+======
Below is a minimal method security configuration when using method security in reactive applications.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity
@@ -370,7 +398,8 @@ public class SecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableReactiveMethodSecurity
@@ -390,12 +419,14 @@ class SecurityConfig {
}
}
----
-====
+======
Consider the following class:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Component
@@ -407,7 +438,8 @@ public class HelloWorldMessageService {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Component
@@ -418,12 +450,14 @@ class HelloWorldMessageService {
}
}
----
-====
+======
Or, the following class using Kotlin coroutines:
-====
-.Kotlin
+[tabs]
+======
+Kotlin::
++
[source,kotlin,role="primary"]
----
@Component
@@ -435,7 +469,7 @@ class HelloWorldMessageService {
}
}
----
-====
+======
Combined with our configuration above, `@PreAuthorize("hasRole('ADMIN')")` will ensure that `findByMessage` is only invoked by a user with the role `ADMIN`.
@@ -445,8 +479,10 @@ This means that the expression must not block.
When integrating with xref:reactive/configuration/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -482,7 +518,8 @@ public class SecurityConfig {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -513,6 +550,6 @@ class SecurityConfig {
}
}
----
-====
+======
You can find a complete sample in {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method]
@@ -14,8 +14,10 @@ You can find a few sample applications that demonstrate the code below:
You can find a minimal WebFlux Security configuration below:
.Minimal WebFlux Security Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
-----
@@ -34,7 +36,8 @@ public class HelloWebfluxSecurityConfig {
}
-----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
-----
@EnableWebFluxSecurity
@@ -51,7 +54,7 @@ class HelloWebfluxSecurityConfig {
}
}
-----
-====
+======
This configuration provides form and http basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default log in page and a default log out page, sets up security related HTTP headers, CSRF protection, and more.
@@ -60,8 +63,10 @@ This configuration provides form and http basic authentication, sets up authoriz
You can find an explicit version of the minimal WebFlux Security configuration below:
.Explicit WebFlux Security Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
-----
@Configuration
@@ -91,7 +96,8 @@ public class HelloWebfluxSecurityConfig {
@@ -35,8 +35,10 @@ These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-s
You can configure `CookieServerCsrfTokenRepository` in Java Configuration using:
.Store CSRF Token in a Cookie
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
-----
@Bean
@@ -48,7 +50,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
}
-----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
-----
@Bean
@@ -61,7 +64,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
-----
-====
+======
[NOTE]
====
@@ -78,8 +81,10 @@ However, it is simple to disable CSRF protection if it xref:features/exploits/cs
The Java configuration below will disable CSRF protection.
.Disable CSRF Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -91,7 +96,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
-----
@Bean
@@ -104,7 +110,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
-----
-====
+======
[[webflux-csrf-configure-request-handler]]
==== Configure ServerCsrfTokenRequestHandler
@@ -117,8 +123,10 @@ An alternate implementation `XorServerCsrfTokenRequestAttributeHandler` is avail
You can configure `XorServerCsrfTokenRequestAttributeHandler` using the following Java configuration:
.Configure BREACH protection
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
-----
@Bean
@@ -132,7 +140,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
}
-----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
-----
@Bean
@@ -145,7 +154,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
-----
-====
+======
[[webflux-csrf-include]]
=== Include the CSRF Token
@@ -161,8 +170,10 @@ If your view technology does not provide a simple way to subscribe to the `Mono<
For example, the following code will place the `CsrfToken` on the default attribute name (`_csrf`) used by Spring Security's <<webflux-csrf-include-form-auto,CsrfRequestDataValueProcessor>> to automatically include the CSRF token as a hidden input.
.`CsrfToken` as `@ModelAttribute`
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@ControllerAdvice
@@ -176,7 +187,8 @@ public class SecurityControllerAdvice {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@ControllerAdvice
@@ -190,7 +202,7 @@ class SecurityControllerAdvice {
}
}
----
-====
+======
Fortunately, Thymeleaf provides <<webflux-csrf-include-form-auto,integration>> that works without any additional work.
@@ -200,14 +212,12 @@ In order to post an HTML form the CSRF token must be included in the form as a h
For example, the rendered HTML might look like:
.CSRF Token HTML
-====
[source,html]
----
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
----
-====
Next we will discuss various ways of including the CSRF token in a form as a hidden input.
@@ -227,7 +237,6 @@ If the <<webflux-csrf-include,other options>> for including the actual CSRF toke
The Thymeleaf sample below assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`.
.CSRF Token in Form with Request Attribute
-====
[source,html]
----
<form th:action="@{/logout}"
@@ -239,7 +248,6 @@ The Thymeleaf sample below assumes that you <<webflux-csrf-include-subscribe,exp
th:value="${_csrf.token}"/>
</form>
----
-====
[[webflux-csrf-include-ajax]]
==== Ajax and JSON Requests
@@ -261,7 +269,6 @@ An alternative pattern to <<webflux-csrf-include-form-auto,exposing the CSRF in
The HTML might look something like this:
.CSRF meta tag HTML
-====
[source,html]
----
<html>
@@ -272,13 +279,11 @@ The HTML might look something like this:
</head>
<!-- ... -->
----
-====
Once the meta tags contained the CSRF token, the JavaScript code would read the meta tags and include the CSRF token as a header.
If you were using jQuery, this could be done with the following:
.AJAX send CSRF Token
-====
[source,javascript]
----
$(function () {
@@ -289,13 +294,11 @@ $(function () {
});
});
----
-====
The sample below assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`.
An example of doing this with Thymeleaf is shown below:
.CSRF meta tag JSP
-====
[source,html]
----
<html>
@@ -307,7 +310,6 @@ An example of doing this with Thymeleaf is shown below:
</head>
<!-- ... -->
----
-====
[[webflux-csrf-considerations]]
== CSRF Considerations
@@ -339,8 +341,10 @@ For example, the following Java Configuration will perform logout with the URL `
// FIXME: This should be a link to log out documentation
.Log out with HTTP GET
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -352,7 +356,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -365,7 +370,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
[[webflux-considerations-csrf-timeouts]]
@@ -401,8 +406,10 @@ We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already d
In a WebFlux application, this can be configured with the following configuration:
.Enable obtaining CSRF token from multipart/form-data
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -414,7 +421,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -427,7 +435,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
[[webflux-csrf-considerations-multipart-url]]
==== Include CSRF Token in URL
@@ -437,14 +445,12 @@ Since the `CsrfToken` is exposed as an `ServerHttpRequest` <<webflux-csrf-includ
@@ -38,10 +41,12 @@ You can add Spring Security to your Spring Boot project by adding `spring-boot-s
You can now https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-running-with-the-maven-plugin[run the Spring Boot application] by using the Maven Plugin's `run` goal.
The following example shows how to do so (and the beginning of the output from doing so):
-.Running Spring Boot Application
-====
-.Maven
+.Running Spring Boot Application
+[tabs]
+======
+Maven::
++
[source,bash,role="primary"]
----
$ ./mvnw spring-boot:run
@@ -53,7 +58,8 @@ Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336
...
----
-.Gradle
+Gradle::
++
[source,bash,role="secondary"]
----
$ ./gradlew bootRun
@@ -64,7 +70,7 @@ Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336
@@ -14,8 +14,10 @@ You can find a few sample applications that demonstrate the code below:
You can find a minimal RSocket Security configuration below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
-----
@Configuration
@@ -34,7 +36,8 @@ public class HelloRSocketSecurityConfig {
}
-----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -51,7 +54,7 @@ open class HelloRSocketSecurityConfig {
}
}
----
-====
+======
This configuration enables <<rsocket-authentication-simple,simple authentication>> and sets up <<rsocket-authorization,rsocket-authorization>> to require an authenticated user for any request.
@@ -61,8 +64,10 @@ For Spring Security to work we need to apply `SecuritySocketAcceptorInterceptor`
This is what connects our `PayloadSocketAcceptorInterceptor` we created with the RSocket infrastructure.
In a Spring Boot application this is done automatically using `RSocketSecurityAutoConfiguration` with the following code.
@@ -83,7 +89,7 @@ fun springSecurityRSocketSecurity(interceptor: SecuritySocketAcceptorInterceptor
}
}
----
-====
+======
[[rsocket-authentication]]
== RSocket Authentication
@@ -123,8 +129,10 @@ See `RSocketSecurity.basicAuthentication(Customizer)` for setting it up.
The RSocket receiver can decode the credentials using `AuthenticationPayloadExchangeConverter` which is automatically setup using the `simpleAuthentication` portion of the DSL.
@@ -238,7 +256,7 @@ open fun findRadar(code: String): Mono<AirportLocation> {
}
}
----
-====
+======
[[rsocket-authentication-jwt]]
=== JWT
@@ -249,8 +267,10 @@ The support comes in the form of authenticating a JWT (determining the JWT is va
The RSocket receiver can decode the credentials using `BearerPayloadExchangeConverter` which is automatically setup using the `jwt` portion of the DSL.
@@ -111,8 +111,10 @@ OPTIONAL. Space delimited, case sensitive list of ASCII string values that speci
The following example shows how to configure the `DefaultServerOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -154,7 +156,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -192,7 +195,7 @@ class SecurityConfig {
}
}
----
-====
+======
For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.
@@ -217,8 +220,10 @@ Alternatively, if your requirements are more advanced, you can take full control
The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
@@ -275,7 +283,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -291,7 +300,7 @@ class OAuth2ClientSecurityConfig {
}
}
----
-====
+======
=== Requesting an Access Token
@@ -327,8 +336,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveAuthorizationCodeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
.Access Token Response Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -354,7 +365,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -377,7 +389,7 @@ class OAuth2ClientSecurityConfig {
}
}
----
-====
+======
[[oauth2Client-refresh-token-grant]]
@@ -421,8 +433,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,
@@ -504,8 +519,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveClientCredentialsTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`,
@@ -564,8 +582,10 @@ spring:
...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -587,7 +607,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -603,12 +624,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -632,7 +655,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class OAuth2ClientController {
@@ -654,7 +678,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
`ServerWebExchange` is an OPTIONAL attribute.
@@ -701,8 +725,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactivePasswordTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
@@ -846,12 +877,14 @@ private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<Mut
}
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -875,7 +908,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -897,7 +931,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
`ServerWebExchange` is an OPTIONAL attribute.
@@ -943,8 +977,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveJwtBearerTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
...and the `OAuth2AuthorizedClientManager` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -1031,7 +1070,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -1048,12 +1088,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RestController
@@ -1075,7 +1117,8 @@ public class OAuth2ResourceServerController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class OAuth2ResourceServerController {
@@ -1094,7 +1137,7 @@ class OAuth2ResourceServerController {
}
}
----
-====
+======
[NOTE]
`JwtBearerReactiveOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.
The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -24,7 +26,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -37,7 +40,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses a <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager>> and therefore inherits it's capabilities.
@@ -58,8 +61,10 @@ It directly uses an <<oauth2Client-authorized-manager-provider, ReactiveOAuth2Au
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
The JWT produced by `NimbusJwtClientAuthenticationParametersConverter` contains the `iss`, `sub`, `aud`, `jti`, `iat` and `exp` claims by default. You can customize the headers and/or claims by providing a `Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>>` to `setJwtClientAssertionCustomizer()`. The following example shows how to customize claims of the JWT:
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
----
-====
+======
The above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.
@@ -106,8 +109,10 @@ The auto-configuration also registers the `ReactiveClientRegistrationRepository`
The following listing shows an example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -125,7 +130,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -142,7 +148,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[[oauth2Client-authorized-client]]
== OAuth2AuthorizedClient
@@ -163,8 +169,10 @@ From a developer perspective, the `ServerOAuth2AuthorizedClientRepository` or `R
The following listing shows an example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -183,7 +191,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -201,7 +210,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
Spring Boot 2.x auto-configuration registers an `ServerOAuth2AuthorizedClientRepository` and/or `ReactiveOAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.
@@ -235,8 +244,10 @@ The `ReactiveOAuth2AuthorizedClientProviderBuilder` may be used to configure and
The following code shows an example of how to configure and build a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -261,7 +272,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -280,7 +292,7 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
When an authorization attempt succeeds, the `DefaultReactiveOAuth2AuthorizedClientManager` will delegate to the `ReactiveOAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `ServerOAuth2AuthorizedClientRepository`.
In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `ServerOAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler`.
@@ -291,8 +303,10 @@ This can be useful when you need to supply a `ReactiveOAuth2AuthorizedClientProv
The following code shows an example of the `contextAttributesMapper`:
@@ -376,7 +391,7 @@ private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<Mut
}
}
----
-====
+======
The `DefaultReactiveOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `ServerWebExchange`.
When operating *_outside_* of a `ServerWebExchange` context, use `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` instead.
@@ -387,8 +402,10 @@ An OAuth 2.0 Client configured with the `client_credentials` grant type can be c
The following code shows an example of how to configure an `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -410,7 +427,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
@@ -24,8 +24,10 @@ The `ServerHttpSecurity.oauth2Client()` DSL provides a number of configuration o
The following code shows the complete configuration options provided by the `ServerHttpSecurity.oauth2Client()` DSL:
.OAuth2 Client Configuration Options
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -47,7 +49,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -67,14 +70,16 @@ class OAuth2ClientSecurityConfig {
}
}
----
-====
+======
The `ReactiveOAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `ReactiveOAuth2AuthorizedClientProvider`(s).
The following code shows an example of how to register a `ReactiveOAuth2AuthorizedClientManager` `@Bean` and associate it with a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -99,7 +104,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
@@ -495,8 +515,10 @@ If you need to customize the pre-processing of the UserInfo Request and/or the p
Whether you customize `DefaultReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService`, you'll need to configure it as shown in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -518,7 +540,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -537,7 +560,7 @@ class OAuth2LoginSecurityConfig {
@@ -551,8 +574,10 @@ If you need to customize the pre-processing of the UserInfo Request and/or the p
Whether you customize `OidcReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService` for OpenID Connect 1.0 Provider's, you'll need to configure it as shown in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -574,7 +599,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -593,7 +619,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
[[webflux-oauth2-login-advanced-idtoken-verify]]
@@ -610,8 +636,10 @@ The JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` a
The following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -622,7 +650,8 @@ public ReactiveJwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -632,7 +661,7 @@ fun idTokenDecoderFactory(): ReactiveJwtDecoderFactory<ClientRegistration> {
return idTokenDecoderFactory
}
----
-====
+======
[NOTE]
For MAC based algorithms such as `HS256`, `HS384` or `HS512`, the `client-secret` corresponding to the `client-id` is used as the symmetric key for signature verification.
@@ -668,8 +697,10 @@ spring:
...and the `OidcClientInitiatedServerLogoutSuccessHandler`, which implements RP-Initiated Logout, may be configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -705,7 +736,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -737,7 +769,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
NOTE: `OidcClientInitiatedServerLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder.
If used, the application's base URL, like `https://app.example.org`, will replace it at request time.
<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties.
<2> Following the base property prefix is the ID for the xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration[`ClientRegistration`], such as google.
-====
. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier.
@@ -244,8 +242,10 @@ If you need to override the auto-configuration based on your specific requiremen
The following example shows how to register a `ReactiveClientRegistrationRepository` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary",attrs="-attributes"]
----
@Configuration
@@ -275,7 +275,8 @@ public class OAuth2LoginConfig {
The following example shows how to register a `SecurityWebFilterChain` `@Bean` with `@EnableWebFluxSecurity` and enable OAuth 2.0 login through `serverHttpSecurity.oauth2Login()`:
.OAuth2 Login Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -333,7 +336,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@@ -350,7 +354,7 @@ class OAuth2LoginSecurityConfig {
@@ -359,8 +363,10 @@ class OAuth2LoginSecurityConfig {
The following example shows how to completely override the auto-configuration by registering a `ReactiveClientRegistrationRepository` `@Bean` and a `SecurityWebFilterChain` `@Bean`.
.Overriding the auto-configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary",attrs="-attributes"]
----
@EnableWebFluxSecurity
@@ -401,7 +407,8 @@ public class OAuth2LoginConfig {
If you are not able to use Spring Boot 2.x and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration:
.OAuth2 Login Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -493,7 +502,8 @@ public class OAuth2LoginConfig {
@@ -10,8 +10,10 @@ For example, you may have a need to read the bearer token from a custom header.
To achieve this, you can wire an instance of `ServerBearerTokenAuthenticationConverter` into the DSL, as you can see in the following example:
.Custom Bearer Token Header
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
ServerBearerTokenAuthenticationConverter converter = new ServerBearerTokenAuthenticationConverter();
@@ -22,7 +24,8 @@ http
);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val converter = ServerBearerTokenAuthenticationConverter()
@@ -33,15 +36,17 @@ return http {
}
}
----
-====
+======
== Bearer Token Propagation
Now that you're in possession of a bearer token, it might be handy to pass that to downstream services.
This is quite simple with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServerBearerExchangeFilterFunction.html[ServerBearerExchangeFilterFunction]`, which you can see in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -52,7 +57,8 @@ public WebClient rest() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -62,15 +68,17 @@ fun rest(): WebClient {
.build()
}
----
-====
+======
When the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any `{security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]` credential.
Then, it will propagate that token in the `Authorization` header.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
this.rest.get()
@@ -79,7 +87,8 @@ this.rest.get()
.bodyToMono(String.class)
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
this.rest.get()
@@ -87,14 +96,16 @@ this.rest.get()
.retrieve()
.bodyToMono<String>()
----
-====
+======
Will invoke the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.
In places where you need to override this behavior, it's a simple matter of supplying the header yourself, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
this.rest.get()
@@ -104,7 +115,8 @@ this.rest.get()
.bodyToMono(String.class)
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
rest.get()
@@ -113,7 +125,7 @@ rest.get()
.retrieve()
.bodyToMono<String>()
----
-====
+======
In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
@@ -112,8 +112,10 @@ There are two ``@Bean``s that Spring Boot generates on Resource Server's behalf.
The first is a `SecurityWebFilterChain` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `SecurityWebFilterChain` looks like:
Calling `{security-api-url}org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.html#fromIssuerLocation-java.lang.String-[ReactiveJwtDecoders#fromIssuerLocation]` is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.
@@ -223,8 +232,10 @@ And its configuration can be overridden using `jwkSetUri()` or replaced using `d
An authorization server's JWK Set Uri can be configured <<webflux-oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:
which is responsible for converting a `Jwt` into an `Authentication`.
As part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.
That final converter might be something like `GrantedAuthoritiesExtractor` below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
static class GrantedAuthoritiesExtractor
@@ -714,7 +764,8 @@ static class GrantedAuthoritiesExtractor
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
internal class GrantedAuthoritiesExtractor : Converter<Jwt, Collection<GrantedAuthority>> {
@@ -727,12 +778,14 @@ internal class GrantedAuthoritiesExtractor : Converter<Jwt, Collection<GrantedAu
}
}
----
-====
+======
For more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, Mono<AbstractAuthenticationToken>>`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
static class CustomAuthenticationConverter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {
@@ -742,7 +795,8 @@ static class CustomAuthenticationConverter implements Converter<Jwt, Mono<Abstra
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
internal class CustomAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
@@ -751,7 +805,7 @@ internal class CustomAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthe
}
}
----
-====
+======
[[webflux-oauth2resourceserver-jwt-validation]]
=== Configuring Validation
@@ -770,8 +824,10 @@ This can cause some implementation heartburn as the number of collaborating serv
Resource Server uses `JwtTimestampValidator` to verify a token's validity window, and it can be configured with a `clockSkew` to alleviate the above problem:
@@ -17,8 +17,10 @@ In each case, there are two things that need to be done and trade-offs associate
One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, this can be done with the `JwtIssuerReactiveAuthenticationManagerResolver`, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver
@@ -33,7 +35,8 @@ http
);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo")
@@ -47,7 +50,7 @@ return http {
}
}
----
-====
+======
This is nice because the issuer endpoints are loaded lazily.
In fact, the corresponding `JwtReactiveAuthenticationManager` is instantiated only when the first request with the corresponding issuer is sent.
@@ -58,8 +61,10 @@ This allows for an application startup that is independent from those authorizat
Of course, you may not want to restart the application each time a new tenant is added.
In this case, you can configure the `JwtIssuerReactiveAuthenticationManagerResolver` with a repository of `ReactiveAuthenticationManager` instances, which you can edit at runtime, like so:
In this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` with a strategy for obtaining the `ReactiveAuthenticationManager` given the issuer.
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
@@ -188,15 +200,17 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
If the application doesn't expose a `SecurityWebFilterChain` bean, then Spring Boot will expose the above default one.
Replacing this is as simple as exposing the bean within the application:
.Replacing SecurityWebFilterChain
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -218,7 +232,8 @@ public class MyCustomSecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -236,7 +251,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
@@ -244,8 +259,10 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
For example, the second `@Bean` Spring Boot creates is a `ReactiveOpaqueTokenIntrospector`, which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -254,7 +271,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -262,7 +280,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
If the application doesn't expose a `ReactiveOpaqueTokenIntrospector` bean, then Spring Boot will expose the above default one.
@@ -273,8 +291,10 @@ And its configuration can be overridden using `introspectionUri()` and `introspe
An authorization server's Introspection Uri can be configured <<webflux-oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -296,7 +316,8 @@ public class DirectlyConfiguredIntrospectionUri {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -314,7 +335,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
Using `introspectionUri()` takes precedence over any configuration property.
@@ -323,8 +344,10 @@ Using `introspectionUri()` takes precedence over any configuration property.
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of `ReactiveOpaqueTokenIntrospector`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@@ -345,7 +368,8 @@ public class DirectlyConfiguredIntrospector {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -362,7 +386,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
-====
+======
This is handy when deeper configuration, like <<webflux-oauth2resourceserver-opaque-authorization-extraction,authority mapping>>or <<webflux-oauth2resourceserver-opaque-jwt-introspector,JWT revocation>> is necessary.
@@ -371,8 +395,10 @@ This is handy when deeper configuration, like <<webflux-oauth2resourceserver-opa
Or, exposing a `ReactiveOpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -381,7 +407,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -389,7 +416,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
@@ -478,8 +511,10 @@ Then Resource Server would generate an `Authentication` with two authorities, on
This can, of course, be customized using a custom `ReactiveOpaqueTokenIntrospector` that takes a look at the attribute set and converts in its own way:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class CustomAuthoritiesOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
@@ -501,7 +536,8 @@ public class CustomAuthoritiesOpaqueTokenIntrospector implements ReactiveOpaqueT
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class CustomAuthoritiesOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -521,12 +557,14 @@ class CustomAuthoritiesOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector
}
}
----
-====
+======
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -535,7 +573,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -543,7 +582,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
In this case, you can create a custom `ReactiveOpaqueTokenIntrospector` that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class JwtOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
@@ -602,7 +643,8 @@ public class JwtOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospect
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class JwtOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -625,12 +667,14 @@ class JwtOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
}
}
----
-====
+======
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -639,7 +683,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -647,7 +692,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return JwtOpaqueTokenIntrospector()
}
----
-====
+======
[[webflux-oauth2resourceserver-opaque-userinfo]]
== Calling a `/userinfo` Endpoint
@@ -663,8 +708,10 @@ This implementation below does three things:
* Looks up the appropriate client registration associated with the `/userinfo` endpoint
* Invokes and returns the response from the `/userinfo` endpoint
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
@@ -693,7 +740,8 @@ public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntro
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -716,13 +764,15 @@ class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
}
}
----
-====
+======
If you aren't using `spring-security-oauth2-client`, it's still quite simple.
You will simply need to invoke the `/userinfo` with your own instance of `WebClient`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
@@ -738,7 +788,8 @@ public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntro
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -751,12 +802,14 @@ class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
}
}
----
-====
+======
Either way, having created your `ReactiveOpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:
For example, we can test our example from xref:reactive/authorization/method.adoc#jc-erms[EnableReactiveMethodSecurity] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[Testing Method Security].
Here is a minimal sample of what we can do:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@ExtendWith(SpringExtension.class)
@@ -39,7 +41,8 @@ public class HelloWorldMessageServiceTests {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@ExtendWith(SpringExtension.class)
@@ -72,4 +75,4 @@ class HelloWorldMessageServiceTests {
After xref:reactive/test/web/setup.adoc[applying the Spring Security support to `WebTestClient`] we can use either annotations or `mutateWith` support.
@@ -112,6 +115,6 @@ fun messageWhenMutateWithMockAdminThenOk() {
.expectBody<String>().isEqualTo("Hello World!")
}
----
-====
+======
In addition to `mockUser()`, Spring Security ships with several other convenience mutators for things like xref:reactive/test/web/csrf.adoc[CSRF] and xref:reactive/test/web/oauth2.adoc[OAuth 2.0].
=== When I try to log in, I get an error message that says "Bad Credentials". What's wrong?
@@ -196,8 +196,10 @@ This will be different in different companies, so you have to find it out yourse
Before adding a Spring Security LDAP configuration to an application, it's a good idea to write a simple test using standard Java LDAP code (without Spring Security involved), and make sure you can get that to work first.
For example, to authenticate a user, you could use the following code:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@@ -216,7 +218,8 @@ public void ldapAuthenticationIsSuccessful() throws Exception {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -230,7 +233,7 @@ fun ldapAuthenticationIsSuccessful() {
val ctx = InitialLdapContext(env, null)
}
----
-====
+======
=== Session Management
@@ -516,8 +519,10 @@ To load the data from an alternative source, you must be using an explicitly dec
You can't use the namespace.
You would then implement `FilterInvocationSecurityMetadataSource` to load the data as you please for a particular `FilterInvocation` footnote:[The `FilterInvocation` object contains the `HttpServletRequest`, so you can obtain the URL or any other relevant information on which to base your decision on what the list of returned attributes will contain.]. A very basic outline would look something like this:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@@ -546,7 +551,8 @@ You would then implement `FilterInvocationSecurityMetadataSource` to load the da
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource {
@@ -569,7 +575,7 @@ class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource {
}
}
----
-====
+======
For more information, look at the code for `DefaultFilterInvocationSecurityMetadataSource`.
@@ -582,8 +588,10 @@ The `DefaultLdapAuthoritiesPopulator` loads the user authorities from the LDAP d
To use JDBC instead, you can implement the interface yourself, using whatever SQL is appropriate for your schema:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@@ -609,7 +617,8 @@ To use JDBC instead, you can implement the interface yourself, using whatever SQ
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class MyAuthoritiesPopulator : LdapAuthoritiesPopulator {
@@ -629,7 +638,7 @@ class MyAuthoritiesPopulator : LdapAuthoritiesPopulator {
}
}
----
-====
+======
You would then add a bean of this type to your application context and inject it into the `LdapAuthenticationProvider`. This is covered in the section on configuring LDAP using explicit Spring beans in the LDAP chapter of the reference manual.
Note that you can't use the namespace for configuration in this case.
@@ -647,8 +656,10 @@ More information can be found in the https://docs.spring.io/spring/docs/3.0.x/sp
Normally, you would add the functionality you require to the `postProcessBeforeInitialization` method of `BeanPostProcessor`. Let's say that you want to customize the `AuthenticationDetailsSource` used by the `UsernamePasswordAuthenticationFilter`, (created by the `form-login` element). You want to extract a particular header called `CUSTOM_HEADER` from the request and make use of it while authenticating the user.
The processor class would look like this:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@@ -674,7 +685,8 @@ public class CustomBeanPostProcessor implements BeanPostProcessor {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class CustomBeanPostProcessor : BeanPostProcessor {
@@ -692,7 +704,7 @@ class CustomBeanPostProcessor : BeanPostProcessor {
}
}
----
-====
+======
You would then register this bean in your application context.
Spring will automatically invoke it on the beans defined in the application context.
<1> We start by creating an empty `SecurityContext`.
It is important to create a new `SecurityContext` instance instead of using `SecurityContextHolder.getContext().setAuthentication(authentication)` to avoid race conditions across multiple threads.
@@ -66,8 +69,10 @@ Spring Security will use this information for xref:servlet/authorization/index.a
If you wish to obtain information about the authenticated principal, you can do so by accessing the `SecurityContextHolder`.
Then, you can use Spring's `@EventListener` support:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Component
@@ -48,7 +53,8 @@ public class AuthenticationEvents {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Component
@@ -64,7 +70,7 @@ class AuthenticationEvents {
}
}
----
-====
+======
While similar to `AuthenticationSuccessHandler` and `AuthenticationFailureHandler`, these are nice in that they can be used independently from the servlet API.
@@ -89,8 +95,10 @@ The publisher does an exact `Exception` match, which means that sub-classes of t
To that end, you may want to supply additional mappings to the publisher via the `setAdditionalExceptionMappings` method:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -106,7 +114,8 @@ public AuthenticationEventPublisher authenticationEventPublisher
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -119,14 +128,16 @@ fun authenticationEventPublisher
return authenticationEventPublisher
}
----
-====
+======
== Default Event
And, you can supply a catch-all event to fire in the case of any `AuthenticationException`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -140,7 +151,8 @@ public AuthenticationEventPublisher authenticationEventPublisher
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -151,4 +163,4 @@ fun authenticationEventPublisher
expirationTime: The date and time when the nonce expires, expressed in milliseconds
key: A private key to prevent modification of the nonce token
----
-====
You will need to ensure you xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`.
The following provides an example of configuring Digest Authentication with Java Configuration:
@@ -8,8 +8,10 @@ Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentic
In this sample we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`.
.InMemoryUserDetailsManager Java Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary",attrs="-attributes"]
----
@Bean
@@ -28,7 +30,8 @@ public UserDetailsService users() {
}
----
-.XML
+XML::
++
[source,xml,role="secondary",attrs="-attributes"]
----
<user-service>
@@ -41,7 +44,8 @@ public UserDetailsService users() {
@@ -59,7 +63,7 @@ fun users(): UserDetailsService {
return InMemoryUserDetailsManager(user, admin)
}
----
-====
+======
The samples above store the passwords in a secure format, but leave a lot to be desired in terms of getting started experience.
@@ -69,8 +73,10 @@ However, it does not protect against obtaining the password by decompiling the s
For this reason, `User.withDefaultPasswordEncoder` should only be used for "getting started" and is not intended for production.
.InMemoryUserDetailsManager with User.withDefaultPasswordEncoder
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -91,7 +97,8 @@ public UserDetailsService users() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -111,13 +118,12 @@ fun users(): UserDetailsService {
return InMemoryUserDetailsManager(user, admin)
}
----
-====
+======
There is no simple way to use `User.withDefaultPasswordEncoder` with XML based configuration.
For demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe-format[no encoding should be used].
.<user-service> `+{noop}+` XML Configuration
-====
[source,xml,attrs="-attributes"]
----
<user-service>
@@ -129,4 +135,3 @@ For demos or just getting started, you can choose to prefix the password with `+
@@ -108,8 +102,10 @@ Before we configure `JdbcUserDetailsManager`, we must create a `DataSource`.
In our example, we will setup an https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#jdbc-embedded-database-support[embedded DataSource] that is initialized with the <<servlet-authentication-jdbc-schema,default user schema>>.
.Embedded Data Source
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -121,7 +117,8 @@ DataSource dataSource() {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<jdbc:embedded-database>
@@ -129,7 +126,8 @@ DataSource dataSource() {
</jdbc:embedded-database>
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -140,7 +138,7 @@ fun dataSource(): DataSource {
.build()
}
----
-====
+======
In a production environment, you will want to ensure you setup a connection to an external database.
@@ -151,9 +149,11 @@ In this sample we use xref:features/authentication/password-storage.adoc#authent
See the xref:features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords.
@@ -256,8 +273,10 @@ This is done by creating an LDAP `ContextSource`, which is the equivalent of a J
If you have already configured an `EmbeddedLdapServerContextSourceFactoryBean`, Spring Security will create an LDAP `ContextSource` that points to the embedded LDAP server.
.LDAP Context Source with Embedded LDAP Server
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -269,7 +288,8 @@ public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -279,13 +299,15 @@ fun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {
return contextSourceFactoryBean
}
----
-====
+======
Alternatively, you can explicitly configure the LDAP `ContextSource` to connect to the supplied LDAP server.
@@ -365,15 +393,17 @@ fun authenticationManager(contextSource: BaseLdapPathContextSource): Authenticat
return factory.createAuthenticationManager()
}
----
-====
+======
This simple example would obtain the DN for the user by substituting the user login name in the supplied pattern and attempting to bind as that user with the login password.
This is OK if all your users are stored under a single node in the directory.
If instead you wished to configure an LDAP search filter to locate the user, you could use the following:
@@ -404,7 +436,7 @@ fun authenticationManager(contextSource: BaseLdapPathContextSource): Authenticat
return factory.createAuthenticationManager()
}
----
-====
+======
If used with the `ContextSource` <<servlet-authentication-ldap-contextsource,definition above>>, this would perform a search under the DN `ou=people,dc=springframework,dc=org` using `+(uid={0})+` as a filter.
Again the user login name is substituted for the parameter in the filter name, so it will search for an entry with the `uid` attribute equal to the user name.
@@ -418,8 +450,10 @@ This can either be done by retrieving the value of the password attribute and ch
An LDAP compare cannot be done when the password is properly hashed with a random salt.
Upon authenticating the user, the user is associated to a new session id to prevent xref:servlet/authentication/session-management.adoc#ns-session-fixation[session fixation attacks].
@@ -89,8 +83,10 @@ When the error dispatch is made, there is no `SecurityContext` established.
This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.
.Use RequestAttributeSecurityContextRepository
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public SecurityFilterChain filterChain(HttpSecurity http) {
@@ -103,7 +99,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
@@ -122,8 +119,10 @@ The {security-api-url}org/springframework/security/web/context/DelegatingSecurit
The most useful arrangement for this is configured with the following example, which allows the use of both xref:requestattributesecuritycontextrepository[`RequestAttributeSecurityContextRepository`] and xref:httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`] simultaneously.
@@ -30,7 +29,6 @@ expirationTime: The date and time when the remember-me token expires, express
key: A private key to prevent modification of the remember-me token
algorithmName: The algorithm used to generate and to verify the remember-me token signature
----
-====
As such the remember-me token is valid only for the period specified, and provided that the username, password and key does not change.
Notably, this has a potential security issue in that a captured remember-me token will be usable from any user agent until such time as the token expires.
@@ -41,7 +39,6 @@ Alternatively, remember-me services should simply not be used at all.
If you are familiar with the topics discussed in the chapter on xref:servlet/configuration/xml-namespace.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `<remember-me>` element:
-====
[source,xml]
----
<http>
@@ -49,7 +46,6 @@ If you are familiar with the topics discussed in the chapter on xref:servlet/con
<remember-me key="myAppKey"/>
</http>
----
-====
The `UserDetailsService` will normally be selected automatically.
If you have more than one in your application context, you need to specify which one should be used with the `user-service-ref` attribute, where the value is the name of your `UserDetailsService` bean.
@@ -60,7 +56,6 @@ This approach is based on the article https://web.archive.org/web/20180819014446
There is a discussion on this in the comments section of this article.].
To use the this approach with namespace configuration, you would supply a datasource reference:
-====
[source,xml]
----
<http>
@@ -68,11 +63,9 @@ To use the this approach with namespace configuration, you would supply a dataso
<remember-me data-source-ref="someDataSource"/>
</http>
----
-====
The database should contain a `persistent_logins` table, created using the following SQL (or equivalent):
-====
[source,ddl]
----
create table persistent_logins (username varchar(64) not null,
Please refer to the Javadoc for a fuller discussion on what the methods do, although note at this stage that `AbstractAuthenticationProcessingFilter` only calls the `loginFail()` and `loginSuccess()` methods.
The `autoLogin()` method is called by `RememberMeAuthenticationFilter` whenever the `SecurityContextHolder` does not contain an `Authentication`.
@@ -122,8 +112,10 @@ If no `algorithmName` is present, the default matching algorithm will be used, w
You can specify different algorithms for signature encoding and for signature matching, this allows users to safely upgrade to a different encoding algorithm while still able to verify old ones if there is no `algorithmName` present.
To do that you can specify your customized `TokenBasedRememberMeServices` as a Bean and use it in the configuration.
The following beans are required in an application context to enable remember-me services:
-====
[source,xml]
----
<bean id="rememberMeFilter" class=
@@ -185,7 +178,6 @@ The following beans are required in an application context to enable remember-me
<property name="key" value="springRocks"/>
</bean>
----
-====
Don't forget to add your `RememberMeServices` implementation to your `UsernamePasswordAuthenticationFilter.setRememberMeServices()` property, include the `RememberMeAuthenticationProvider` in your `AuthenticationManager.setProviders()` list, and add `RememberMeAuthenticationFilter` into your `FilterChainProxy` (typically immediately after your `UsernamePasswordAuthenticationFilter`).
@@ -6,8 +6,10 @@ Once you have got an application that is xref:servlet/authentication/index.adoc[
This is done automatically by default, so no additional code is necessary, though there are some steps you should consider. The first is setting the `requireExplicitSave` property in `HttpSecurity`.
You can do it like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -21,7 +23,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -36,14 +39,15 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http security-context-explicit-save="true">
<!-- ... -->
</http>
----
-====
+======
The most straightforward reason for this is that it is xref:migration/servlet/session-management.adoc#_require_explicit_saving_of_securitycontextrepository[becoming the default value in 6.0], so this will make sure you are ready for that.
@@ -100,8 +104,10 @@ This means that there is no need to detect when `Authentication` is done and thu
To opt into the new Spring Security 6 default, the following configuration should be used.
<1> Add the `SecurityContextRepository` to the controller
<2> Inject the `HttpServletRequest` and `HttpServletResponse` to be able to save the `SecurityContext`
@@ -280,14 +294,16 @@ The reason is that it doesn't remove it from the `SecurityContextRepository`, wh
To make sure the authentication is properly cleared and saved, you can invoke {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[the `SecurityContextLogoutHandler`] which does that for us, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
SecurityContextLogoutHandler handler = new SecurityContextLogoutHandler(); <1>
<1> Create a new instance of `SecurityContextLogoutHandler`
<2> Call the `logout` method passing in the `HttpServletRequest`, `HttpServletResponse` and a `null` authentication because it is not required for this handler.
@@ -302,8 +318,10 @@ Some authentication mechanisms like xref:servlet/authentication/passwords/basic.
If you do not wish to create sessions, you can use `SessionCreationPolicy.STATELESS`, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -317,7 +335,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -332,19 +351,20 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http create-session="stateless">
<!-- ... -->
</http>
----
-====
+======
The above configuration is <<customizing-where-authentication-is-stored, configuring the `SecurityContextRepository`>> to use a `NullSecurityContextRepository` and is also xref:servlet/architecture.adoc#requestcache-prevent-saved-request[preventing the request from being saved in the session].
-[[never-policy-session-still-created]]
+[[never-policy-session-still-created]]
If you are using `SessionCreationPolicy.NEVER`, you might notice that the application is still creating a `HttpSession`.
In most cases, this happens because the xref:servlet/architecture.adoc#savedrequests[request is saved in the session] for the authenticated resource to re-request after authentication is successful.
To avoid that, please refer to xref:servlet/architecture.adoc#requestcache-prevent-saved-request[how to prevent the request of being saved] section.
@@ -358,8 +378,10 @@ If, for some reason, you are using a stateless authentication mechanism, but you
For the xref:servlet/authentication/passwords/basic.adoc[HTTP Basic], you can add xref:servlet/configuration/java.adoc#post-processing-configured-objects[a `ObjectPostProcessor`] that changes the `SecurityContextRepository` used by the `BasicAuthenticationFilter`:
.Store HTTP Basic authentication in the `HttpSession`
The above also applies to others authentication mechanisms, like xref:servlet/oauth2/resource-server/index.adoc[Bearer Token Authentication].
@@ -408,8 +430,10 @@ In summary, when `requireExplicitSave` is `true`, Spring Security sets up xref:s
If you wish to place constraints on a single user's ability to log in to your application, Spring Security supports this out of the box with the following simple additions.
First, you need to add the following listener to your configuration to keep Spring Security updated about session lifecycle events:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -418,7 +442,8 @@ public HttpSessionEventPublisher httpSessionEventPublisher() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -427,7 +452,8 @@ open fun httpSessionEventPublisher(): HttpSessionEventPublisher {
}
----
-.web.xml
+web.xml::
++
[source,xml,role="secondary"]
----
<listener>
@@ -436,12 +462,14 @@ open fun httpSessionEventPublisher(): HttpSessionEventPublisher {
</listener-class>
</listener>
----
-====
+======
Then add the following lines to your security configuration:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -454,7 +482,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -470,7 +499,8 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -480,15 +510,17 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
</session-management>
</http>
----
-====
+======
This will prevent a user from logging in multiple times - a second login will cause the first to be invalidated.
Using Spring Boot, you can test the above configuration scenario the following way:
@@ -607,7 +645,7 @@ public class MaximumSessionsPreventLoginTests {
}
----
-====
+======
If you are using a customized authentication filter for form-based login, then you have to configure concurrent session control support explicitly.
You can try it using the {gh-samples-url}/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login[Maximum Sessions Prevent Login sample].
@@ -619,8 +657,10 @@ That said, Spring Security can detect when a session has expired and take specif
For example, you may want to redirect to a specific endpoint when a user makes a request with an already-expired session.
This is achieved through the `invalidSessionUrl` in `HttpSecurity`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -633,7 +673,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -647,7 +688,8 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -655,7 +697,7 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
Note that if you use this mechanism to detect session timeouts, it may falsely report an error if the user logs out and then logs back in without closing the browser.
This is because the session cookie is not cleared when you invalidate the session and will be resubmitted even if the user has logged out.
@@ -666,8 +708,10 @@ If that is your case, you might want to <<clearing-session-cookie-on-logout,conf
The `invalidSessionUrl` is a convenience method for setting the `InvalidSessionStrategy` using the {security-api-url}/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.html[`SimpleRedirectInvalidSessionStrategy` implementation].
If you want to customize the behavior, you can implement the {security-api-url}/org/springframework/security/web/session/InvalidSessionStrategy.html[`InvalidSessionStrategy`] interface and configure it using the `invalidSessionStrategy` method:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -680,7 +724,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -694,7 +739,8 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -703,15 +749,17 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
You can explicitly delete the JSESSIONID cookie on logging out, for example by using the https://w3c.github.io/webappsec-clear-site-data/[`Clear-Site-Data` header] in the logout handler:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -724,7 +772,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -738,7 +787,8 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
@@ -756,14 +806,16 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
</b:bean>
</http>
----
-====
+======
This has the advantage of being container agnostic and will work with any container that supports the `Clear-Site-Data` header.
As an alternative, you can also use the following syntax in the logout handler:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -776,7 +828,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -790,14 +843,15 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<http>
<logout delete-cookies="JSESSIONID" />
</http>
----
-====
+======
Unfortunately, this cannot be guaranteed to work with every servlet container, so you need to test it in your environment.
@@ -807,14 +861,12 @@ If you run your application behind a proxy, you may also be able to remove the s
For example, by using Apache HTTPD's `mod_headers`, the following directive deletes the `JSESSIONID` cookie by expiring it in the response to a logout request (assuming the application is deployed under the `/tutorial` path):
=====
-====
[source,xml]
----
<LocationMatch "/tutorial/logout">
Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT"
</LocationMatch>
----
-====
More details on the xref:servlet/exploits/headers.adoc#servlet-headers-clear-site-data[Clear Site Data] and xref:servlet/authentication/logout.adoc[Logout sections].
@@ -843,8 +895,10 @@ This is the default in Servlet 3.0 or older containers.
You can configure the session fixation protection by doing:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -859,7 +913,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -875,14 +930,15 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
When session fixation protection occurs, it results in a `SessionFixationProtectionEvent` being published in the application context.
If you use `changeSessionId`, this protection will __also__ result in any ``jakarta.servlet.http.HttpSessionIdListener``s being notified, so use caution if your code listens for both events.
@@ -896,8 +952,10 @@ You can also set the session fixation protection to `none` to disable it, but th
Consider the following block of code:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
1. Creates an empty `SecurityContext` instance by accessing the `SecurityContextHolder` statically.
2. Sets the `Authentication` object in the `SecurityContext` instance.
@@ -923,8 +981,10 @@ By default, they will still look up the strategy from `SecurityContextHolder`.
These changes are largely internal, but they present the opportunity for applications to autowire the `SecurityContextHolderStrategy` instead of accessing the `SecurityContext` statically.
To do so, you should change the code to the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class SomeClass {
@@ -943,7 +1003,7 @@ public class SomeClass {
}
----
-====
+======
1. Creates an empty `SecurityContext` instance using the configured `SecurityContextHolderStrategy`.
2. Sets the `Authentication` object in the `SecurityContext` instance.
@@ -956,8 +1016,10 @@ public class SomeClass {
At times, it can be valuable to eagerly create sessions.
This can be done by using the {security-api-url}org/springframework/security/web/session/ForceEagerSessionCreationFilter.html[`ForceEagerSessionCreationFilter`] which can be configured using:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -970,7 +1032,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -984,14 +1047,15 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
Here we have four roles in a hierarchy `ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST`.
A user who is authenticated with `ROLE_ADMIN`, will behave as if they have all four roles when security constraints are evaluated against an `AuthorizationManager` adapted to call the above `RoleHierarchyVoter`.
@@ -16,8 +16,10 @@ You can override the default when you declare a `SecurityFilterChain`.
Instead of using xref:servlet/authorization/authorize-http-requests.adoc#servlet-authorize-requests-defaults[`authorizeRequests`], use `authorizeHttpRequests`, like so:
<1> There are multiple authorization rules specified.
Each rule is considered in the order they were declared.
<2> We specified multiple URL patterns that any user can access.
@@ -91,8 +95,10 @@ This is a good strategy if you do not want to accidentally forget to update your
You can take a bean-based approach by constructing your own xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[`RequestMatcherDelegatingAuthorizationManager`] like so:
You can also wire xref:servlet/authorization/architecture.adoc#authz-custom-authorization-manager[your own custom authorization managers] for any request matcher.
@@ -135,8 +141,10 @@ You can also wire xref:servlet/authorization/architecture.adoc#authz-custom-auth
Here is an example of mapping a custom authorization manager to the `my/authorized/endpoint`:
@@ -206,14 +220,16 @@ open fun web(http: HttpSecurity): SecurityFilterChain {
return http.build()
}
----
-====
+======
Now with the authorization rules applying to all dispatcher types, you have more control of the authorization on them.
For example, you may want to configure `shouldFilterAllDispatcherTypes` to `true` but not apply authorization on requests with dispatcher type `ASYNC` or `FORWARD`.
If you are referring to a bean in your expression like so: `@webSecurity.check(authentication, request)`, it's recommended that you instead call the bean directly, which will look something like the following:
For complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`.
In this configuration URLs that match would pass in the path variable (and convert it) into checkUserId method.
For example, if the URL were `/user/123/resource`, then the id passed in would be `123`.
@@ -260,41 +274,47 @@ Their use is enabled through the `global-method-security` namespace element:
The most obviously useful annotation is `@PreAuthorize` which decides whether a method can actually be invoked or not.
For example (from the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application)
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@PreAuthorize("hasRole('USER')")
public void create(Contact contact);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasRole('USER')")
fun create(contact: Contact?)
----
-====
+======
which means that access will only be allowed for users with the role "ROLE_USER".
Obviously the same thing could easily be achieved using a traditional configuration and a simple configuration attribute for the required role.
But what about:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasPermission(#contact, 'admin')")
fun deletePermission(contact: Contact?, recipient: Sid?, permission: Permission?)
----
-====
+======
Here we're actually using a method argument as part of the expression to decide whether the current user has the "admin" permission for the given contact.
The built-in `hasPermission()` expression is linked into the Spring Security ACL module through the application context, as we'll <<el-permission-evaluator,see below>>.
Here we are accessing another built-in expression, `authentication`, which is the `Authentication` stored in the security context.
You can also access its "principal" property directly, using the expression `principal`.
@@ -417,8 +446,10 @@ Spring Security supports filtering of collections, arrays, maps and streams usin
This is most commonly performed on the return value of a method.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@PreAuthorize("hasRole('USER')")
@@ -426,14 +457,15 @@ For example:
public List<Contact> getAll();
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
fun getAll(): List<Contact?>
----
-====
+======
When using the `@PostFilter` annotation, Spring Security iterates through the returned collection or map and removes any elements for which the supplied expression is false.
For an array, a new array instance will be returned containing filtered elements.
@@ -507,8 +539,10 @@ For example, consider the following:
Instead of repeating this everywhere, we can create a meta annotation that can be used instead.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@@ -516,14 +550,15 @@ Instead of repeating this everywhere, we can create a meta annotation that can b
@@ -704,12 +762,14 @@ open class MethodSecurityConfig {
// ...
}
----
-====
+======
and the equivalent Java code would be
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public interface BankService {
@@ -725,7 +785,8 @@ public Account post(Account account, double amount);
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
interface BankService {
@@ -739,7 +800,7 @@ interface BankService {
fun post(account: Account, amount: Double): Account
}
----
-====
+======
== GlobalMethodSecurityConfiguration
@@ -747,8 +808,10 @@ Sometimes you may need to perform operations that are more complicated than are
For these instances, you can extend the `GlobalMethodSecurityConfiguration` ensuring that the `@EnableGlobalMethodSecurity` annotation is present on your subclass.
For example, if you wanted to provide a custom `MethodSecurityExpressionHandler`, you could use the following configuration:
@@ -772,7 +836,7 @@ open class MethodSecurityConfig : GlobalMethodSecurityConfiguration() {
}
}
----
-====
+======
For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc.
@@ -791,8 +855,10 @@ Adding an annotation to a method (on an class or interface) would then limit the
Spring Security's native annotation support defines a set of attributes for the method.
These will be passed to the `AccessDecisionManager` for it to make the actual decision:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public interface BankService {
@@ -809,7 +875,8 @@ public Account post(Account account, double amount);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
interface BankService {
@@ -823,7 +890,7 @@ interface BankService {
fun post(account: Account, amount: Double): Account
}
----
-====
+======
Support for JSR-250 annotations can be enabled using
@@ -842,8 +909,10 @@ To use the new expression-based syntax, you would use
and the equivalent Java code would be
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public interface BankService {
@@ -859,7 +928,8 @@ public Account post(Account account, double amount);
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
interface BankService {
@@ -873,7 +943,7 @@ interface BankService {
fun post(account: Account, amount: Double): Account
}
----
-====
+======
Expression-based annotations are a good choice if you need to define simple rules that go beyond checking the role names against the user's list of authorities.
You can configure `XorCsrfTokenRequestAttributeHandler` in Java Configuration using the following:
.Configure BREACH protection
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -207,7 +209,8 @@ public class WebSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -224,7 +227,7 @@ class SecurityConfig {
}
}
----
-====
+======
[[servlet-csrf-include]]
=== Include the CSRF Token
@@ -242,14 +245,12 @@ In order to post an HTML form the CSRF token must be included in the form as a h
For example, the rendered HTML might look like:
.CSRF Token HTML
-====
[source,html]
----
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
----
-====
Next we will discuss various ways of including the CSRF token in a form as a hidden input.
@@ -273,7 +274,6 @@ If the <<servlet-csrf-include,other options>> for including the actual CSRF toke
An example of doing this with a JSP is shown below:
.CSRF Token in Form with Request Attribute
-====
[source,xml]
----
<c:url var="logoutUrl" value="/logout"/>
@@ -286,7 +286,6 @@ An example of doing this with a JSP is shown below:
value="${_csrf.token}"/>
</form>
----
-====
[[servlet-csrf-include-ajax]]
==== Ajax and JSON Requests
@@ -308,7 +307,6 @@ An alternative pattern to <<servlet-csrf-include-form-auto,exposing the CSRF in
The HTML might look something like this:
.CSRF meta tag HTML
-====
[source,html]
----
<html>
@@ -319,13 +317,11 @@ The HTML might look something like this:
</head>
<!-- ... -->
----
-====
Once the meta tags contained the CSRF token, the JavaScript code would read the meta tags and include the CSRF token as a header.
If you were using jQuery, this could be done with the following:
.AJAX send CSRF Token
-====
[source,javascript]
----
$(function () {
@@ -336,7 +332,6 @@ $(function () {
});
});
----
-====
[[servlet-csrf-include-ajax-meta-tag]]
====== csrfMeta tag
@@ -350,7 +345,6 @@ If the <<servlet-csrf-include,other options>> for including the actual CSRF toke
An example of doing this with a JSP is shown below:
.CSRF meta tag JSP
-====
[source,html]
----
<html>
@@ -362,7 +356,6 @@ An example of doing this with a JSP is shown below:
</head>
<!-- ... -->
----
-====
[[servlet-csrf-considerations]]
== CSRF Considerations
@@ -392,8 +385,10 @@ If you really want to use HTTP GET with logout you can do so, but remember this
For example, the following Java Configuration will perform logout with the URL `/logout` is requested with any HTTP method:
.Log out with HTTP GET
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -410,7 +405,8 @@ public class WebSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -427,7 +423,7 @@ class SecurityConfig {
}
}
----
-====
+======
[[servlet-considerations-csrf-timeouts]]
@@ -474,8 +470,10 @@ In general, this is the recommended approach because the temporary file upload s
To ensure `MultipartFilter` is specified before the Spring Security filter with java configuration, users can override beforeSpringSecurityFilterChain as shown below:
.Initializer MultipartFilter
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@@ -487,7 +485,8 @@ public class SecurityApplicationInitializer extends AbstractSecurityWebApplicati
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {
@@ -496,12 +495,11 @@ class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer
}
}
----
-====
+======
To ensure `MultipartFilter` is specified before the Spring Security filter with XML configuration, users can ensure the <filter-mapping> element of the `MultipartFilter` is placed before the springSecurityFilterChain within the web.xml as shown below:
.web.xml - MultipartFilter
-====
[source,xml]
----
<filter>
@@ -521,7 +519,6 @@ To ensure `MultipartFilter` is specified before the Spring Security filter with
<url-pattern>/*</url-pattern>
</filter-mapping>
----
-====
[[servlet-csrf-considerations-multipart-url]]
==== Include CSRF Token in URL
@@ -531,14 +528,12 @@ Since the `CsrfToken` is exposed as an `HttpServletRequest` <<servlet-csrf-inclu
@@ -46,8 +46,10 @@ However, it is important that you do so knowing that this can open your applicat
For example, if you wish to leverage Spring MVC's Matrix Variables, the following configuration could be used:
.Allow Matrix Variables
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -58,7 +60,8 @@ public StrictHttpFirewall httpFirewall() {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<b:bean id="httpFirewall"
@@ -68,7 +71,8 @@ public StrictHttpFirewall httpFirewall() {
<http-firewall ref="httpFirewall"/>
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -78,7 +82,7 @@ fun httpFirewall(): StrictHttpFirewall {
return firewall
}
----
-====
+======
The `StrictHttpFirewall` provides an allowed list of valid HTTP methods that are allowed to protect against https://owasp.org/www-community/attacks/Cross_Site_Tracing[Cross Site Tracing (XST)] and https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods[HTTP Verb Tampering].
The default valid methods are "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", and "PUT".
@@ -87,8 +91,10 @@ For example, the following will only allow HTTP "GET" and "POST" methods:
.Allow Only GET & POST
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -99,7 +105,8 @@ public StrictHttpFirewall httpFirewall() {
}
----
-.XML
+XML::
++
[source,xml,role="secondary"]
----
<b:bean id="httpFirewall"
@@ -109,7 +116,8 @@ public StrictHttpFirewall httpFirewall() {
<http-firewall ref="httpFirewall"/>
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -119,7 +127,7 @@ fun httpFirewall(): StrictHttpFirewall {
return firewall
}
----
-====
+======
[TIP]
====
@@ -132,8 +140,8 @@ See https://jira.spring.io/browse/SPR-16851[SPR_16851] for an issue requesting t
If you must allow any HTTP method (not recommended), you can use `StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)`.
This will disable validation of the HTTP method entirely.
-[[servlet-httpfirewall-headers-parameters]]
+[[servlet-httpfirewall-headers-parameters]]
`StrictHttpFirewall` also checks header names and values and parameter names.
It requires that each character have a defined code point and not be a control character.
@@ -148,8 +156,10 @@ NOTE: Also, parameter values can be controlled with `setAllowedParameterValues(P
For example, to switch off this check, you can wire your `StrictHttpFirewall` with ``Predicate``s that always return `true`, like so:
.Allow Any Header Name, Header Value, and Parameter Name
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -162,7 +172,8 @@ public StrictHttpFirewall httpFirewall() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -174,7 +185,7 @@ fun httpFirewall(): StrictHttpFirewall {
return firewall
}
----
-====
+======
Or, there might be a specific value that you need to allow.
@@ -184,8 +195,10 @@ Due to this fact, some application servers will parse this value into two separa
You can address this with the `setAllowedHeaderValues` method, as you can see below:
.Allow Certain User Agents
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -198,7 +211,8 @@ public StrictHttpFirewall httpFirewall() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -210,13 +224,15 @@ fun httpFirewall(): StrictHttpFirewall {
return firewall
}
----
-====
+======
In the case of header values, you may instead consider parsing them as UTF-8 at verification time like so:
@@ -8,8 +8,10 @@ If the request does not contain any cookies and Spring Security is first, the re
The easiest way to ensure that CORS is handled first is to use the `CorsFilter`.
Users can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource` using the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -36,7 +38,8 @@ public class WebSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -62,7 +65,7 @@ open class WebSecurityConfig {
}
}
----
-====
+======
or in XML
@@ -79,8 +82,10 @@ or in XML
If you are using Spring MVC's CORS support, you can omit specifying the `CorsConfigurationSource` and Spring Security will leverage the CORS configuration provided to Spring MVC.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -98,7 +103,8 @@ public class WebSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -115,7 +121,7 @@ open class WebSecurityConfig {
@@ -9,8 +9,10 @@ It is not only useful but necessary to include the user in the queries to suppor
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -19,7 +21,8 @@ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -27,7 +30,7 @@ fun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {
return SecurityEvaluationContextExtension()
}
----
-====
+======
In XML Configuration, this would look like:
@@ -42,8 +45,10 @@ In XML Configuration, this would look like:
Now Spring Security can be used within your queries.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Repository
@@ -53,7 +58,8 @@ public interface MessageRepository extends PagingAndSortingRepository<Message,Lo
@@ -56,8 +56,10 @@ For a `web.xml` this means that you should place your configuration in the `Disp
Below `WebSecurityConfiguration` in placed in the ``DispatcherServlet``s `ApplicationContext`.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class SecurityInitializer extends
@@ -81,7 +83,8 @@ public class SecurityInitializer extends
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
@@ -101,7 +104,7 @@ class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer
}
}
----
-====
+======
[NOTE]
====
@@ -114,26 +117,31 @@ This is what is known as https://en.wikipedia.org/wiki/Defense_in_depth_(computi
Consider a controller that is mapped as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RequestMapping("/admin")
public String admin() {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RequestMapping("/admin")
fun admin(): String {
----
-====
+======
If we wanted to restrict access to this controller method to admin users, a developer can provide authorization rules by matching on the `HttpServletRequest` with the following:
@@ -159,7 +168,7 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
return http.build()
}
----
-====
+======
or in XML
@@ -182,8 +191,10 @@ Therefore, it will protect the same URLs that Spring MVC will match on by using
One common requirement when using Spring MVC is to specify the servlet path property, for that you can use the `MvcRequestMatcher.Builder` to create multiple `MvcRequestMatcher` instances that share the same servlet path:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -198,7 +209,8 @@ public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospe
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -213,7 +225,7 @@ open fun filterChain(http: HttpSecurity, introspector: HandlerMappingIntrospecto
return http.build()
}
----
-====
+======
[[mvc-authentication-principal]]
@@ -237,8 +249,10 @@ Once `AuthenticationPrincipalArgumentResolver` is properly configured, you can b
Consider a situation where a custom `UserDetailsService` that returns an `Object` that implements `UserDetails` and your own `CustomUser` `Object`. The `CustomUser` of the currently authenticated user could be accessed using the following code:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RequestMapping("/messages/inbox")
@@ -251,7 +265,8 @@ public ModelAndView findMessagesForUser() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RequestMapping("/messages/inbox")
@@ -262,12 +277,14 @@ open fun findMessagesForUser(): ModelAndView {
// .. find messages for this user and return them ...
}
----
-====
+======
As of Spring Security 3.2 we can resolve the argument more directly by adding an annotation. For example:
@@ -281,7 +298,8 @@ public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser cust
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RequestMapping("/messages/inbox")
@@ -290,15 +308,17 @@ open fun findMessagesForUser(@AuthenticationPrincipal customUser: CustomUser?):
// .. find messages for this user and return them ...
}
----
-====
+======
Sometimes it may be necessary to transform the principal in some way.
For example, if `CustomUser` needed to be final it could not be extended.
In this situation the `UserDetailsService` might returns an `Object` that implements `UserDetails` and provides a method named `getCustomUser` to access `CustomUser`.
For example, it might look like:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class CustomUserUserDetails extends User {
@@ -309,7 +329,8 @@ public class CustomUserUserDetails extends User {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class CustomUserUserDetails(
@@ -321,12 +342,14 @@ class CustomUserUserDetails(
val customUser: CustomUser? = null
}
----
-====
+======
We could then access the `CustomUser` using a https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[SpEL expression] that uses `Authentication.getPrincipal()` as the root object:
We can further remove our dependency on Spring Security by making `@AuthenticationPrincipal` a meta annotation on our own annotation.
Below we demonstrate how we could do this on an annotation named `@CurrentUser`.
@@ -404,8 +431,10 @@ Below we demonstrate how we could do this on an annotation named `@CurrentUser`.
NOTE: It is important to realize that in order to remove the dependency on Spring Security, it is the consuming application that would create `@CurrentUser`.
This step is not strictly required, but assists in isolating your dependency to Spring Security to a more central location.
@@ -424,13 +454,15 @@ public @interface CurrentUser {}
@AuthenticationPrincipal
annotation class CurrentUser
----
-====
+======
Now that `@CurrentUser` has been specified, we can use it to signal to resolve our `CustomUser` of the currently authenticated user.
We have also isolated our dependency on Spring Security to a single file.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RequestMapping("/messages/inbox")
@@ -440,7 +472,8 @@ public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RequestMapping("/messages/inbox")
@@ -449,7 +482,7 @@ open fun findMessagesForUser(@CurrentUser customUser: CustomUser?): ModelAndView
// .. find messages for this user and return them ...
}
----
-====
+======
[[mvc-async]]
@@ -459,8 +492,10 @@ Spring Web MVC 3.2+ has excellent support for https://docs.spring.io/spring/docs
With no additional configuration, Spring Security will automatically setup the `SecurityContext` to the `Thread` that invokes a `Callable` returned by your controllers.
For example, the following method will automatically have its `Callable` invoked with the `SecurityContext` that was available when the `Callable` was created:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RequestMapping(method=RequestMethod.POST)
@@ -475,7 +510,8 @@ return new Callable<String>() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RequestMapping(method = [RequestMethod.POST])
@@ -486,7 +522,7 @@ open fun processUpload(file: MultipartFile?): Callable<String> {
}
}
----
-====
+======
[NOTE]
.Associating SecurityContext to Callable's
@@ -554,8 +590,10 @@ If you use XML based configuration, you must add this yourself.
Once `CsrfTokenArgumentResolver` is properly configured, you can expose the `CsrfToken` to your static HTML based application.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RestController
@@ -568,7 +606,8 @@ public class CsrfController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@RestController
@@ -579,7 +618,7 @@ class CsrfController {
}
}
----
-====
+======
It is important to keep the `CsrfToken` a secret from other domains.
This means if you are using https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS[Cross Origin Sharing (CORS)], you should **NOT** expose the `CsrfToken` to any external domains.
val isAdmin: Boolean = httpServletRequest.isUserInRole("ADMIN")
----
-====
+======
This might be useful to determine if certain UI components should be displayed.
For example, you might display admin links only if the current user is an admin.
@@ -93,8 +99,10 @@ If they are not authenticated, the configured AuthenticationEntryPoint will be u
The https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29[HttpServletRequest.login(String,String)] method can be used to authenticate the user with the current `AuthenticationManager`.
For example, the following would attempt to authenticate with the username "user" and password "password":
@@ -135,8 +144,10 @@ The https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%2
Using Spring Security's concurrency support, Spring Security overrides the AsyncContext.start(Runnable) to ensure that the current SecurityContext is used when processing the Runnable.
For example, the following would output the current user's Authentication:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
final AsyncContext async = httpServletRequest.startAsync();
@@ -155,7 +166,8 @@ async.start(new Runnable() {
});
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val async: AsyncContext = httpServletRequest.startAsync()
@@ -171,7 +183,7 @@ async.start {
}
}
----
-====
+======
[[servletapi-async]]
=== Async Servlet Support
@@ -217,8 +229,10 @@ Prior to Spring Security 3.2, the SecurityContext from the SecurityContextHolder
@@ -30,8 +30,10 @@ In Spring Security 5.8, this support has been refreshed to use the `Authorizatio
To configure authorization using Java Configuration, simply include the `@EnableWebSocketSecurity` annotation and publish an `AuthorizationManager<Message<?>>` bean or in XML use the `use-authorization-manager` attribute.
One way to do this is by using the `AuthorizationManagerMessageMatcherRegistry` to specify endpoint patterns like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -48,7 +50,8 @@ public class WebSocketSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -62,14 +65,15 @@ open class WebSocketSecurityConfig { // <1> <2>
When using `AuthorizationManager`, customization is quite simple.
For example, you can publish an `AuthorizationManager` that requires that all messages have a role of "USER" using `AuthorityAuthorizationManager`, as seen below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -97,7 +103,8 @@ public class WebSocketSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -110,19 +117,22 @@ open class WebSocketSecurityConfig {
On the other hand, if you are using the <<legacy-websocket-configuration,legacy `AbstractSecurityWebSocketMessageBrokerConfigurer`>> and you want to allow other domains to access your site, you can disable Spring Security's protection.
For example, in Java Configuration you can use the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -416,7 +437,8 @@ public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBro
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -429,7 +451,7 @@ open class WebSocketSecurityConfig : AbstractSecurityWebSocketMessageBrokerConfi
}
}
----
-====
+======
[[websocket-expression-handler]]
=== Custom Expression Handler
@@ -495,8 +517,10 @@ For example, the following will instruct Spring Security to use "X-Frame-Options
Similarly, you can customize frame options to use the same origin within Java Configuration using the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -516,7 +540,8 @@ public class WebSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -535,7 +560,7 @@ open class WebSecurityConfig {
}
}
----
-====
+======
[[websocket-sockjs-csrf]]
=== SockJS & Relaxing CSRF
@@ -554,8 +579,10 @@ We can easily achieve this by providing a CSRF RequestMatcher.
Our Java Configuration makes this extremely easy.
For example, if our stomp endpoint is "/chat" we can disable CSRF protection for only URLs that start with "/chat/" using the following configuration:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -581,7 +608,8 @@ public class WebSecurityConfig {
...
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -604,7 +632,7 @@ open class WebSecurityConfig {
// ...
----
-====
+======
If we are using XML based configuration, we can use the xref:servlet/appendix/namespace/http.adoc#nsa-csrf-request-matcher-ref[csrf@request-matcher-ref].
For example:
@@ -640,8 +668,10 @@ For example:
Before Spring Security 5.8, the way to configure messaging authorization using Java Configuration, was to extend the `AbstractSecurityWebSocketMessageBrokerConfigurer` and configure the `MessageSecurityMetadataSourceRegistry`.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Configuration
@@ -655,7 +685,8 @@ public class WebSocketSecurityConfig
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Configuration
@@ -665,7 +696,7 @@ open class WebSocketSecurityConfig : AbstractSecurityWebSocketMessageBrokerConfi
@@ -111,8 +111,10 @@ OPTIONAL. Space delimited, case sensitive list of ASCII string values that speci
The following example shows how to configure the `DefaultOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -156,7 +158,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -197,7 +200,7 @@ class SecurityConfig {
}
}
----
-====
+======
For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.
@@ -222,8 +225,10 @@ Alternatively, if your requirements are more advanced, you can take full control
The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
@@ -246,7 +252,7 @@ private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationReques
}
}
----
-====
+======
=== Storing the Authorization Request
@@ -261,8 +267,10 @@ The default implementation of `AuthorizationRequestRepository` is `HttpSessionOA
If you have a custom implementation of `AuthorizationRequestRepository`, you may configure it as shown in the following example:
.AuthorizationRequestRepository Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -291,7 +299,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -311,7 +320,8 @@ class OAuth2ClientSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -320,7 +330,7 @@ class OAuth2ClientSecurityConfig {
</oauth2-client>
</http>
----
-====
+======
=== Requesting an Access Token
@@ -351,8 +361,10 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultAuthorizationCodeTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
The default `RestOperations` is configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
@@ -362,7 +374,8 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
@@ -384,8 +397,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultAuthorizationCodeTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
.Access Token Response Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -405,7 +420,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -425,7 +441,8 @@ class OAuth2ClientSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -434,7 +451,7 @@ class OAuth2ClientSecurityConfig {
</oauth2-client>
</http>
----
-====
+======
[[oauth2Client-refresh-token-grant]]
@@ -473,8 +490,10 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultRefreshTokenTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
The default `RestOperations` is configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
@@ -484,7 +503,8 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
@@ -505,8 +525,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultRefreshTokenTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
`OAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenOAuth2AuthorizedClientProvider`,
@@ -584,8 +607,10 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultClientCredentialsTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
The default `RestOperations` is configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
@@ -595,7 +620,8 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
@@ -616,8 +642,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultClientCredentialsTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
`OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsOAuth2AuthorizedClientProvider`,
@@ -676,8 +705,10 @@ spring:
...and the `OAuth2AuthorizedClientManager` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -699,7 +730,8 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -715,12 +747,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -752,7 +786,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class OAuth2ClientController {
@@ -780,7 +815,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
@@ -823,8 +858,10 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultPasswordTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
The default `RestOperations` is configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
@@ -834,7 +871,8 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
@@ -855,8 +893,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultPasswordTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
@@ -998,12 +1043,14 @@ private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableM
}
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -1035,7 +1082,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -1063,7 +1111,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
@@ -1104,8 +1152,10 @@ If you prefer to only add additional parameters, you can provide `JwtBearerGrant
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultJwtBearerTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
The default `RestOperations` is configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
@@ -1115,7 +1165,8 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
@@ -1136,8 +1187,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultJwtBearerTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
...and the `OAuth2AuthorizedClientManager` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -1224,7 +1280,8 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -1241,12 +1298,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
You may obtain the `OAuth2AccessToken` as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@RestController
@@ -1269,7 +1328,8 @@ public class OAuth2ResourceServerController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class OAuth2ResourceServerController {
@@ -1290,7 +1350,7 @@ class OAuth2ResourceServerController {
}
}
----
-====
+======
[NOTE]
`JwtBearerOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.
The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -25,7 +27,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -40,7 +43,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and therefore inherits it's capabilities.
@@ -61,8 +64,10 @@ It directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
The JWT produced by `NimbusJwtClientAuthenticationParametersConverter` contains the `iss`, `sub`, `aud`, `jti`, `iat` and `exp` claims by default. You can customize the headers and/or claims by providing a `Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>>` to `setJwtClientAssertionCustomizer()`. The following example shows how to customize claims of the JWT:
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
----
-====
+======
The above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.
@@ -106,8 +109,10 @@ The auto-configuration also registers the `ClientRegistrationRepository` as a `@
The following listing shows an example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -128,7 +133,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -148,7 +154,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[[oauth2Client-authorized-client]]
== OAuth2AuthorizedClient
@@ -169,8 +175,10 @@ From a developer perspective, the `OAuth2AuthorizedClientRepository` or `OAuth2A
The following listing shows an example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -193,7 +201,8 @@ public class OAuth2ClientController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -214,7 +223,7 @@ class OAuth2ClientController {
}
}
----
-====
+======
[NOTE]
Spring Boot 2.x auto-configuration registers an `OAuth2AuthorizedClientRepository` and/or `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.
@@ -248,8 +257,10 @@ The `OAuth2AuthorizedClientProviderBuilder` may be used to configure and build t
The following code shows an example of how to configure and build an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -274,7 +285,8 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -293,7 +305,7 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
-====
+======
When an authorization attempt succeeds, the `DefaultOAuth2AuthorizedClientManager` will delegate to the `OAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `OAuth2AuthorizedClientRepository`.
In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `OAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientOAuth2AuthorizationFailureHandler`.
@@ -304,8 +316,10 @@ This can be useful when you need to supply an `OAuth2AuthorizedClientProvider` w
The following code shows an example of the `contextAttributesMapper`:
@@ -387,7 +402,7 @@ private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableM
}
}
----
-====
+======
The `DefaultOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `HttpServletRequest`.
When operating *_outside_* of a `HttpServletRequest` context, use `AuthorizedClientServiceOAuth2AuthorizedClientManager` instead.
@@ -398,8 +413,10 @@ An OAuth 2.0 Client configured with the `client_credentials` grant type can be c
The following code shows an example of how to configure an `AuthorizedClientServiceOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -421,7 +438,8 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
@@ -25,8 +25,10 @@ In addition, `HttpSecurity.oauth2Client().authorizationCodeGrant()` enables the
The following code shows the complete configuration options provided by the `HttpSecurity.oauth2Client()` DSL:
.OAuth2 Client Configuration Options
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -50,7 +52,8 @@ public class OAuth2ClientSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -74,14 +77,13 @@ class OAuth2ClientSecurityConfig {
}
}
----
-====
+======
In addition to the `HttpSecurity.oauth2Client()` DSL, XML configuration is also supported.
The following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-client[ security namespace]:
.OAuth2 Client XML Configuration Options
-====
[source,xml]
----
<http>
@@ -95,14 +97,15 @@ The following code shows the complete configuration options available in the xre
</oauth2-client>
</http>
----
-====
The `OAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `OAuth2AuthorizedClientProvider`(s).
The following code shows an example of how to register an `OAuth2AuthorizedClientManager` `@Bean` and associate it with an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -127,7 +130,8 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
@@ -9,8 +9,10 @@ For example, `oauth2Login().authorizationEndpoint()` allows configuring the _Aut
The following code shows an example:
.Advanced OAuth2 Login Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -38,7 +40,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -66,7 +69,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
The main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications.
@@ -90,8 +93,10 @@ These claims are normally represented by a JSON object that contains a collectio
The following code shows the complete configuration options available for the `oauth2Login()` DSL:
.OAuth2 Login Configuration Options
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -127,7 +132,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -163,14 +169,13 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
In addition to the `oauth2Login()` DSL, XML configuration is also supported.
The following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-login[ security namespace]:
.OAuth2 Login XML Configuration Options
-====
[source,xml]
----
<http>
@@ -190,7 +195,6 @@ The following code shows the complete configuration options available in the xre
jwt-decoder-factory-ref="jwtDecoderFactory"/>
</http>
----
-====
The following sections go into more detail on each of the configuration options available:
@@ -227,8 +231,10 @@ To override the default login page, configure `oauth2Login().loginPage()` and (o
The following listing shows an example:
.OAuth2 Login Page Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -250,7 +256,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -271,7 +278,8 @@ class OAuth2LoginSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -280,7 +288,7 @@ class OAuth2LoginSecurityConfig {
/>
</http>
----
-====
+======
[IMPORTANT]
You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page.
@@ -313,8 +321,10 @@ The default Authorization Response `baseUri` (redirection endpoint) is `*/login/
If you would like to customize the Authorization Response `baseUri`, configure it as shown in the following example:
.Redirection Endpoint Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -334,7 +344,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -354,7 +365,8 @@ class OAuth2LoginSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -363,7 +375,7 @@ class OAuth2LoginSecurityConfig {
/>
</http>
----
-====
+======
[IMPORTANT]
====
@@ -371,7 +383,10 @@ You also need to ensure the `ClientRegistration.redirectUri` matches the custom
@@ -423,8 +440,10 @@ There are a couple of options to choose from when mapping user authorities:
Provide an implementation of `GrantedAuthoritiesMapper` and configure it as shown in the following example:
.Granted Authorities Mapper Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -473,7 +492,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -512,7 +532,8 @@ class OAuth2LoginSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -521,13 +542,15 @@ class OAuth2LoginSecurityConfig {
/>
</http>
----
-====
+======
Alternatively, you may register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example:
.Granted Authorities Mapper Bean Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -547,7 +570,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -567,7 +591,7 @@ class OAuth2LoginSecurityConfig {
==== Delegation-based strategy with OAuth2UserService
@@ -579,8 +603,10 @@ The `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the assoc
The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:
.OAuth2UserService Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -621,7 +647,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -662,7 +689,8 @@ class OAuth2LoginSecurityConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -671,7 +699,7 @@ class OAuth2LoginSecurityConfig {
/>
</http>
----
-====
+======
[[oauth2login-advanced-oauth2-user-service]]
@@ -701,8 +729,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
Whether you customize `DefaultOAuth2UserService` or provide your own implementation of `OAuth2UserService`, you'll need to configure it as shown in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -726,7 +756,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -750,7 +781,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
[[oauth2login-advanced-oidc-user-service]]
@@ -764,8 +795,10 @@ If you need to customize the pre-processing of the UserInfo Request and/or the p
Whether you customize `OidcUserService` or provide your own implementation of `OAuth2UserService` for OpenID Connect 1.0 Provider's, you'll need to configure it as shown in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -789,7 +822,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -813,7 +847,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
[[oauth2login-advanced-idtoken-verify]]
@@ -830,8 +864,10 @@ The JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` a
The following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -842,7 +878,8 @@ public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -852,7 +889,7 @@ fun idTokenDecoderFactory(): JwtDecoderFactory<ClientRegistration?> {
return idTokenDecoderFactory
}
----
-====
+======
[NOTE]
For MAC based algorithms such as `HS256`, `HS384` or `HS512`, the `client-secret` corresponding to the `client-id` is used as the symmetric key for signature verification.
@@ -888,8 +925,10 @@ spring:
...and the `OidcClientInitiatedLogoutSuccessHandler`, which implements RP-Initiated Logout, may be configured as follows:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -924,7 +963,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -956,7 +996,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
-====
+======
NOTE: `OidcClientInitiatedLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder.
If used, the application's base URL, like `https://app.example.org`, will replace it at request time.
<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties.
<2> Following the base property prefix is the ID for the xref:servlet/oauth2/client/index.adoc#oauth2Client-client-registration[ClientRegistration], such as google.
-====
. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier.
@@ -243,8 +241,10 @@ If you need to override the auto-configuration based on your specific requiremen
The following example shows how to register a `ClientRegistrationRepository` `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary",attrs="-attributes"]
----
@Configuration
@@ -274,7 +274,8 @@ public class OAuth2LoginConfig {
The following example shows how to register a `SecurityFilterChain` `@Bean` with `@EnableWebSecurity` and enable OAuth 2.0 login through `httpSecurity.oauth2Login()`:
.OAuth2 Login Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -330,7 +333,8 @@ public class OAuth2LoginSecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -347,7 +351,7 @@ class OAuth2LoginSecurityConfig {
@@ -356,8 +360,10 @@ class OAuth2LoginSecurityConfig {
The following example shows how to completely override the auto-configuration by registering a `ClientRegistrationRepository` `@Bean` and a `SecurityFilterChain` `@Bean`.
.Overriding the auto-configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary",attrs="-attributes"]
----
@Configuration
@@ -397,7 +403,8 @@ public class OAuth2LoginConfig {
If you are not able to use Spring Boot 2.x and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration:
.OAuth2 Login Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -489,7 +498,8 @@ public class OAuth2LoginConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -533,7 +543,8 @@ open class OAuth2LoginConfig {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http auto-config="true">
@@ -557,4 +568,4 @@ open class OAuth2LoginConfig {
Now that your resource server has validated the token, it might be handy to pass it to downstream services.
This is quite simple with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.html[ServletBearerExchangeFilterFunction]`, which you can see in the following example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -111,7 +121,8 @@ public WebClient rest() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -121,15 +132,17 @@ fun rest(): WebClient {
.build()
}
----
-====
+======
When the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any `{security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]` credential.
Then, it will propagate that token in the `Authorization` header.
For example:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
this.rest.get()
@@ -139,7 +152,8 @@ this.rest.get()
.block()
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
this.rest.get()
@@ -148,14 +162,16 @@ this.rest.get()
.bodyToMono<String>()
.block()
----
-====
+======
Will invoke the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.
In places where you need to override this behavior, it's a simple matter of supplying the header yourself, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
this.rest.get()
@@ -166,7 +182,8 @@ this.rest.get()
.block()
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
this.rest.get()
@@ -176,7 +193,7 @@ this.rest.get()
.bodyToMono<String>()
.block()
----
-====
+======
In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
@@ -188,8 +205,10 @@ To obtain this level of support, please use the OAuth 2.0 Client filter.
There is no `RestTemplate` equivalent for `ServletBearerExchangeFilterFunction` at the moment, but you can propagate the request's bearer token quite simply with your own interceptor:
Additionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can xref:servlet/authentication/events.adoc#servlet-events[listen for in your application] like so:
@@ -164,8 +164,10 @@ There are two ``@Bean``s that Spring Boot generates on Resource Server's behalf.
The first is a `SecurityFilterChain` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `SecurityFilterChain` looks like:
@@ -195,15 +198,17 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
return http.build()
}
----
-====
+======
If the application doesn't expose a `SecurityFilterChain` bean, then Spring Boot will expose the above default one.
Replacing this is as simple as exposing the bean within the application:
.Custom JWT Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -225,7 +230,8 @@ public class MyCustomSecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -247,7 +253,7 @@ class MyCustomSecurityConfiguration {
}
}
----
-====
+======
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
@@ -257,8 +263,10 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which <<oauth2resourceserver-jwt-architecture-jwtdecoder,decodes `String` tokens into validated instances of `Jwt`>>:
.JWT Decoder
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -267,7 +275,8 @@ public JwtDecoder jwtDecoder() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -275,7 +284,7 @@ fun jwtDecoder(): JwtDecoder {
return JwtDecoders.fromIssuerLocation(issuerUri)
}
----
-====
+======
[NOTE]
Calling `{security-api-url}org/springframework/security/oauth2/jwt/JwtDecoders.html#fromIssuerLocation-java.lang.String-[JwtDecoders#fromIssuerLocation]` is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.
@@ -289,8 +298,10 @@ Or, if you're not using Spring Boot at all, then both of these components - the
The filter chain is specified like so:
.Default JWT Configuration
-====
-.Xml
+[tabs]
+======
+Xml::
++
[source,xml,role="primary"]
----
<http>
@@ -300,13 +311,15 @@ The filter chain is specified like so:
@@ -323,8 +336,10 @@ And the `JwtDecoder` like so:
An authorization server's JWK Set Uri can be configured <<oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:
.JWK Set Uri Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -345,7 +360,8 @@ public class DirectlyConfiguredJwkSetUri {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -367,7 +383,8 @@ class DirectlyConfiguredJwkSetUri {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -377,7 +394,7 @@ class DirectlyConfiguredJwkSetUri {
</oauth2-resource-server>
</http>
----
-====
+======
Using `jwkSetUri()` takes precedence over any configuration property.
@@ -387,8 +404,10 @@ Using `jwkSetUri()` takes precedence over any configuration property.
More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>>:
.JWT Decoder Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -409,7 +428,8 @@ public class DirectlyConfiguredJwtDecoder {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -431,7 +451,8 @@ class DirectlyConfiguredJwtDecoder {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -441,7 +462,7 @@ class DirectlyConfiguredJwtDecoder {
</oauth2-resource-server>
</http>
----
-====
+======
This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.
@@ -450,8 +471,10 @@ This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validat
Or, exposing a <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> `@Bean` has the same effect as `decoder()`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -460,7 +483,8 @@ public JwtDecoder jwtDecoder() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -468,7 +492,7 @@ fun jwtDecoder(): JwtDecoder {
@@ -840,8 +895,10 @@ Let's say that that your authorization server communicates authorities in a cust
In that case, you can configure the claim that <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,`JwtAuthenticationConverter`>> should inspect, like so:
.Authorities Claim Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -855,7 +912,8 @@ public JwtAuthenticationConverter jwtAuthenticationConverter() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -869,7 +927,8 @@ fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -891,14 +950,16 @@ fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
You can also configure the authority prefix to be different as well.
Instead of prefixing each authority with `SCOPE_`, you can change it to `ROLE_` like so:
.Authorities Prefix Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -912,7 +973,8 @@ public JwtAuthenticationConverter jwtAuthenticationConverter() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -926,7 +988,8 @@ fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -948,14 +1011,16 @@ fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
<property name="authorityPrefix" value="ROLE_"/>
</bean>
----
-====
+======
Or, you can remove the prefix altogether by calling `JwtGrantedAuthoritiesConverter#setAuthorityPrefix("")`.
For more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, AbstractAuthenticationToken>`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
static class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
@@ -984,7 +1049,8 @@ public class CustomAuthenticationConverterConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
internal class CustomAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {
@@ -1013,7 +1079,7 @@ class CustomAuthenticationConverterConfig {
}
}
----
-====
+======
[[oauth2resourceserver-jwt-validation]]
== Configuring Validation
@@ -1032,8 +1098,10 @@ This can cause some implementation heartburn as the number of collaborating serv
Resource Server uses `JwtTimestampValidator` to verify a token's validity window, and it can be configured with a `clockSkew` to alleviate the above problem:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -1051,7 +1119,8 @@ JwtDecoder jwtDecoder() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -1067,7 +1136,7 @@ fun jwtDecoder(): JwtDecoder {
return jwtDecoder
}
----
-====
+======
[NOTE]
By default, Resource Server configures a clock skew of 60 seconds.
@@ -1077,8 +1146,10 @@ By default, Resource Server configures a clock skew of 60 seconds.
Adding a check for <<_supplying_audiences, the `aud` claim>> is simple with the `OAuth2TokenValidator` API:
In more sophisticated scenarios, like consulting multiple claims at once or renaming a claim, Resource Server accepts any class that implements `Converter<Map<String, Object>, Map<String,Object>>`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class UsernameSubClaimAdapter implements Converter<Map<String, Object>, Map<String, Object>> {
@@ -1313,7 +1402,8 @@ public class UsernameSubClaimAdapter implements Converter<Map<String, Object>, M
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class UsernameSubClaimAdapter : Converter<Map<String, Any?>, Map<String, Any?>> {
@@ -101,8 +108,10 @@ In each case, there are two things that need to be done and trade-offs associate
One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, this can be done with the `JwtIssuerAuthenticationManagerResolver`, like so:
.Multi-tenancy Tenant by JWT Claim
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver
@@ -117,7 +126,8 @@ http
);
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
val customAuthenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
@@ -132,7 +142,8 @@ http {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -149,7 +160,7 @@ http {
</constructor-arg>
</bean>
----
-====
+======
This is nice because the issuer endpoints are loaded lazily.
In fact, the corresponding `JwtAuthenticationProvider` is instantiated only when the first request with the corresponding issuer is sent.
@@ -160,8 +171,10 @@ This allows for an application startup that is independent from those authorizat
Of course, you may not want to restart the application each time a new tenant is added.
In this case, you can configure the `JwtIssuerAuthenticationManagerResolver` with a repository of `AuthenticationManager` instances, which you can edit at runtime, like so:
private fun addManager(authenticationManagers: MutableMap<String, AuthenticationManager>, issuer: String) {
@@ -207,7 +221,7 @@ http {
}
}
----
-====
+======
In this case, you construct `JwtIssuerAuthenticationManagerResolver` with a strategy for obtaining the `AuthenticationManager` given the issuer.
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
@@ -221,8 +235,10 @@ You may have observed that this strategy, while simple, comes with the trade-off
This extra parsing can be alleviated by configuring the xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-architecture-jwtdecoder[`JwtDecoder`] directly with a `JWTClaimsSetAwareJWSKeySelector` from Nimbus:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Component
@@ -264,7 +280,8 @@ public class TenantJWSKeySelector
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Component
@@ -301,7 +318,7 @@ class TenantJWSKeySelector(tenants: TenantRepository) : JWTClaimsSetAwareJWSKeyS
}
}
----
-====
+======
<1> A hypothetical source for tenant information
<2> A cache for `JWKKeySelector`s, keyed by tenant identifier
<3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a list of allowed tenants
@@ -315,8 +332,10 @@ Without this, you have no guarantee that the issuer hasn't been altered by a bad
@@ -338,7 +358,7 @@ fun jwtProcessor(keySelector: JWTClaimsSetAwareJWSKeySelector<SecurityContext>):
return jwtProcessor
}
----
-====
+======
As you are already seeing, the trade-off for moving tenant-awareness down to this level is more configuration.
We have just a bit more.
@@ -346,8 +366,10 @@ We have just a bit more.
Next, we still want to make sure you are validating the issuer.
But, since the issuer may be different per JWT, then you'll need a tenant-aware validator, too:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Component
@@ -378,7 +400,8 @@ public class TenantJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Component
@@ -406,12 +429,14 @@ class TenantJwtIssuerValidator(tenants: TenantRepository) : OAuth2TokenValidator
}
}
----
-====
+======
Now that we have a tenant-aware processor and a tenant-aware validator, we can proceed with creating our xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-architecture-jwtdecoder[`JwtDecoder`]:
@@ -214,15 +226,17 @@ open fun filterChain(http: HttpSecurity): SecurityFilterChain {
return http.build()
}
----
-====
+======
If the application doesn't expose a `SecurityFilterChain` bean, then Spring Boot will expose the above default one.
Replacing this is as simple as exposing the bean within the application:
.Custom Opaque Token Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -244,7 +258,8 @@ public class MyCustomSecurityConfiguration {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -266,7 +281,7 @@ class MyCustomSecurityConfiguration {
}
}
----
-====
+======
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
@@ -275,8 +290,10 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
[[oauth2resourceserver-opaque-introspector]]
For example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, <<oauth2resourceserver-opaque-architecture-introspector,which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`>>:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -285,7 +302,8 @@ public OpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -293,7 +311,7 @@ fun introspector(): OpaqueTokenIntrospector {
If the application doesn't expose an <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> bean, then Spring Boot will expose the above default one.
@@ -306,8 +324,10 @@ Or, if you're not using Spring Boot at all, then all of these components - the f
The filter chain is specified like so:
.Default Opaque Token Configuration
-====
-.Xml
+[tabs]
+======
+Xml::
++
[source,xml,role="primary"]
----
<http>
@@ -318,13 +338,15 @@ The filter chain is specified like so:
</oauth2-resource-server>
</http>
----
-====
+======
And the <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> like so:
.Opaque Token Introspector
-====
-.Xml
+[tabs]
+======
+Xml::
++
[source,xml,role="primary"]
----
<bean id="opaqueTokenIntrospector"
@@ -334,19 +356,21 @@ And the <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntr
@@ -354,8 +378,10 @@ And the `OpaqueTokenAuthenticationConverter` like so:
An authorization server's Introspection Uri can be configured <<oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:
.Introspection URI Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -377,7 +403,8 @@ public class DirectlyConfiguredIntrospectionUri {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -400,7 +427,8 @@ class DirectlyConfiguredIntrospectionUri {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<bean id="opaqueTokenIntrospector"
@@ -410,7 +438,7 @@ class DirectlyConfiguredIntrospectionUri {
<constructor-arg value="secret"/>
</bean>
----
-====
+======
Using `introspectionUri()` takes precedence over any configuration property.
@@ -420,8 +448,10 @@ Using `introspectionUri()` takes precedence over any configuration property.
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>:
.Introspector Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -442,7 +472,8 @@ public class DirectlyConfiguredIntrospector {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -464,7 +495,8 @@ class DirectlyConfiguredIntrospector {
}
----
-.Xml
+Xml::
++
[source,xml,role="secondary"]
----
<http>
@@ -474,7 +506,7 @@ class DirectlyConfiguredIntrospector {
</oauth2-resource-server>
</http>
----
-====
+======
This is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.
@@ -503,8 +535,10 @@ When this is the case, Resource Server will attempt to coerce these scopes into
This means that to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:
.Authorization Opaque Token Configuration
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -523,7 +557,8 @@ public class MappedAuthorities {
@@ -595,8 +634,10 @@ Then Resource Server would generate an `Authentication` with two authorities, on
This can, of course, be customized using a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that takes a look at the attribute set and converts in its own way:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
@@ -618,7 +659,8 @@ public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntr
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class CustomAuthoritiesOpaqueTokenIntrospector : OpaqueTokenIntrospector {
@@ -636,12 +678,14 @@ class CustomAuthoritiesOpaqueTokenIntrospector : OpaqueTokenIntrospector {
}
}
----
-====
+======
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -650,7 +694,8 @@ public OpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -658,7 +703,7 @@ fun introspector(): OpaqueTokenIntrospector {
return CustomAuthoritiesOpaqueTokenIntrospector()
}
----
-====
+======
[[oauth2resourceserver-opaque-timeouts]]
== Configuring Timeouts
@@ -670,8 +715,10 @@ Further, it doesn't take into account more sophisticated patterns like back-off
To adjust the way in which Resource Server connects to the authorization server, `NimbusOpaqueTokenIntrospector` accepts an instance of `RestOperations`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -686,7 +733,8 @@ public OpaqueTokenIntrospector introspector(RestTemplateBuilder builder, OAuth2R
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -699,7 +747,7 @@ fun introspector(builder: RestTemplateBuilder, properties: OAuth2ResourceServerP
In this case, you can create a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
@@ -759,7 +809,8 @@ public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class JwtOpaqueTokenIntrospector : OpaqueTokenIntrospector {
@@ -782,12 +833,14 @@ class JwtOpaqueTokenIntrospector : OpaqueTokenIntrospector {
}
}
----
-====
+======
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Bean
@@ -796,7 +849,8 @@ public OpaqueTokenIntrospector introspector() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Bean
@@ -804,7 +858,7 @@ fun introspector(): OpaqueTokenIntrospector {
return JwtOpaqueTokenIntrospector()
}
----
-====
+======
[[oauth2resourceserver-opaque-userinfo]]
== Calling a `/userinfo` Endpoint
@@ -820,8 +874,10 @@ This implementation below does three things:
* Looks up the appropriate client registration associated with the `/userinfo` endpoint
* Invokes and returns the response from the `/userinfo` endpoint
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
@@ -846,7 +902,8 @@ public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
@@ -867,13 +924,15 @@ class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
}
}
----
-====
+======
If you aren't using `spring-security-oauth2-client`, it's still quite simple.
You will simply need to invoke the `/userinfo` with your own instance of `WebClient`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
@@ -889,7 +948,8 @@ public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
@@ -902,12 +962,14 @@ class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
}
}
----
-====
+======
Either way, having created your <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>, you should publish it as a `@Bean` to override the defaults:
@@ -23,8 +23,10 @@ By default, Spring Security uses an `HttpSessionSaml2AuthenticationRequestReposi
If you have a custom implementation of `Saml2AuthenticationRequestRepository`, you may configure it by exposing it as a `@Bean` as shown in the following example:
var relyingPartyRegistration: RelyingPartyRegistration =
@@ -91,7 +98,7 @@ var relyingPartyRegistration: RelyingPartyRegistration =
}
.build();
----
-====
+======
Otherwise, you will need to specify a private key to `RelyingPartyRegistration#signingX509Credentials` so that Spring Security can sign the `<saml2:AuthnRequest>` before sending.
@@ -102,8 +109,10 @@ You can configure the algorithm based on the asserting party's xref:servlet/saml
@@ -20,8 +20,10 @@ To configure these, you'll use the `saml2Login#authenticationManager` method in
To apply a `RelyingPartyRegistrationResolver` when processing `<saml2:Response>` payloads, you should first publish a `Saml2AuthenticationTokenConverter` bean like so:
Or, perhaps you would like to include user details from a legacy `UserDetailsService`.
In that case, the response authentication converter can come in handy, as can be seen below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -200,7 +214,8 @@ public class SecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -232,7 +247,7 @@ open class SecurityConfig {
}
}
----
-====
+======
<1> First, call the default converter, which extracts attributes and authorities from the response
<2> Second, call the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] using the relevant information
<3> Third, return a custom authentication that includes the user details
@@ -276,8 +291,10 @@ To perform additional validation, you can configure your own assertion validator
NOTE: There are two separate decrypters since assertions can be signed separately from responses.
Trying to decrypt a signed assertion's elements before signature verification may invalidate the signature.
@@ -385,8 +409,10 @@ If your asserting party signs the response only, then it's safe to decrypt all e
Of course, the `authenticationManager` DSL method can be also used to perform a completely custom SAML 2.0 authentication.
This authentication manager should expect a `Saml2AuthenticationToken` object containing the SAML 2.0 Response XML data.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@EnableWebSecurity
@@ -408,7 +434,8 @@ public class SecurityConfig {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
@@ -428,7 +455,7 @@ open class SecurityConfig {
}
}
----
-====
+======
[[servlet-saml2login-authenticatedprincipal]]
== Using `Saml2AuthenticatedPrincipal`
@@ -438,8 +465,10 @@ Once the relying party validates an assertion, the result is a `Saml2Authenticat
This means that you can access the principal in your controller like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Controller
@@ -453,7 +482,8 @@ public class MainController {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Controller
@@ -466,7 +496,7 @@ class MainController {
}
}
----
-====
+======
[TIP]
Because the SAML 2.0 specification allows for each attribute to have multiple values, you can either call `getAttribute` to get the list of attributes or `getFirstAttribute` to get the first in the list.
@@ -188,8 +188,10 @@ The resulting `Authentication#getPrincipal` is a Spring Security `Saml2Authentic
Any class that uses both Spring Security and OpenSAML should statically initialize `OpenSamlInitializationService` at the beginning of the class, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
static {
@@ -198,7 +200,8 @@ static {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
companion object {
@@ -207,7 +210,7 @@ companion object {
}
}
----
-====
+======
This replaces OpenSAML's `InitializationService#initialize`.
@@ -217,8 +220,10 @@ In these circumstances, you may instead want to call `OpenSamlInitializationServ
For example, when sending an unsigned AuthNRequest, you may want to force reauthentication.
In that case, you can register your own `AuthnRequestMarshaller`, like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
static {
@@ -245,7 +250,8 @@ static {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
companion object {
@@ -271,7 +277,7 @@ companion object {
}
}
----
-====
+======
The `requireInitialize` method may only be called once per application instance.
@@ -284,8 +290,10 @@ The first is a `SecurityFilterChain` that configures the app as a relying party.
When including `spring-security-saml2-service-provider`, the `SecurityFilterChain` looks like:
=== Resolving the `RelyingPartyRegistration` from the Request
@@ -839,8 +876,10 @@ Remember that if you have any placeholders in your `RelyingPartyRegistration`, y
You can provide a resolver that, for example, always returns the same `RelyingPartyRegistration`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class SingleRelyingPartyRegistrationResolver implements RelyingPartyRegistrationResolver {
@@ -858,7 +897,8 @@ public class SingleRelyingPartyRegistrationResolver implements RelyingPartyRegis
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class SingleRelyingPartyRegistrationResolver(delegate: RelyingPartyRegistrationResolver) : RelyingPartyRegistrationResolver {
@@ -867,7 +907,7 @@ class SingleRelyingPartyRegistrationResolver(delegate: RelyingPartyRegistrationR
}
}
----
-====
+======
[TIP]
You might next take a look at how to use this resolver to customize xref:servlet/saml2/metadata.adoc#servlet-saml2login-metadata[`<saml2:SPSSODescriptor>` metadata production].
@@ -882,8 +922,10 @@ This carries the implication that the assertion consumer service endpoint will b
You can instead resolve the `registrationId` via the `Issuer`.
A custom implementation of `RelyingPartyRegistrationResolver` that does this may look like:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class SamlResponseIssuerRelyingPartyRegistrationResolver implements RelyingPartyRegistrationResolver {
@@ -911,7 +953,8 @@ public class SamlResponseIssuerRelyingPartyRegistrationResolver implements Relyi
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class SamlResponseIssuerRelyingPartyRegistrationResolver(val registrations: InMemoryRelyingPartyRegistrationRepository):
@@ -935,7 +978,7 @@ class SamlResponseIssuerRelyingPartyRegistrationResolver(val registrations: InMe
}
}
----
-====
+======
[TIP]
You might next take a look at how to use this resolver to customize xref:servlet/saml2/login/authentication.adoc#relyingpartyregistrationresolver-apply[`<saml2:Response>` authentication].
@@ -948,8 +991,10 @@ In this case, the identity provider's metadata endpoint returns multiple `<md:ID
These multiple asserting parties can be accessed in a single call to `RelyingPartyRegistrations` like so:
@@ -100,8 +100,10 @@ This URL is customizable in the DSL.
For example, if you are migrating your existing relying party over to Spring Security, your asserting party may already be pointing to `GET /SLOService.saml2`.
To reduce changes in configuration for the asserting party, you can configure the filter in the DSL like so:
Or, if you have registered a custom relying party registration resolver in the constructor, then you can specify a path without a `registrationId` hint, like so:
In the event that you are applying a `RelyingPartyRegistrationResolver` to remove the `registrationId` from the URI, you must also change the URI in the filter like so:
@@ -126,7 +138,7 @@ fun getMessageWithMockUser() {
// ...
}
----
-====
+======
Specifically the following is true:
@@ -139,8 +151,10 @@ Our example is nice because we are able to leverage a lot of defaults.
What if we wanted to run the test with a different username?
The following test would run with the username "customUser". Again, the user does not need to actually exist.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -151,7 +165,8 @@ public void getMessageWithMockUserCustomUsername() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -161,13 +176,15 @@ fun getMessageWithMockUserCustomUsername() {
// ...
}
----
-====
+======
We can also easily customize the roles.
For example, this test will be invoked with the username "admin" and the roles "ROLE_USER" and "ROLE_ADMIN".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -178,7 +195,8 @@ public void getMessageWithMockUserCustomUser() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -188,13 +206,15 @@ fun getMessageWithMockUserCustomUser() {
// ...
}
----
-====
+======
If we do not want the value to automatically be prefixed with ROLE_ we can leverage the authorities attribute.
For example, this test will be invoked with the username "admin" and the authorities "USER" and "ADMIN".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -205,7 +225,8 @@ public void getMessageWithMockUserCustomAuthorities() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -215,14 +236,16 @@ fun getMessageWithMockUserCustomUsername() {
// ...
}
----
-====
+======
Of course it can be a bit tedious placing the annotation on every test method.
Instead, we can place the annotation at the class level and every test will use the specified user.
For example, the following would run every test with a user with the username "admin", the password "password", and the roles "ROLE_USER" and "ROLE_ADMIN".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@ExtendWith(SpringExtension.class)
@@ -231,7 +254,8 @@ For example, the following would run every test with a user with the username "a
public class WithMockUserTests {
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@ExtendWith(SpringExtension.class)
@@ -239,13 +263,15 @@ public class WithMockUserTests {
If you are using JUnit 5's `@Nested` test support, you can also place the annotation on the enclosing class to apply to all nested classes.
For example, the following would run every test with a user with the username "admin", the password "password", and the roles "ROLE_USER" and "ROLE_ADMIN" for both test methods.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@ExtendWith(SpringExtension.class)
@@ -265,7 +291,8 @@ public class WithMockUserTests {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@ExtendWith(SpringExtension::class)
@@ -281,7 +308,7 @@ class WithMockUserTests {
}
}
----
-====
+======
By default the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.
This is the equivalent of happening before JUnit's `@Before`.
@@ -300,8 +327,10 @@ Using `@WithAnonymousUser` allows running as an anonymous user.
This is especially convenient when you wish to run most of your tests with a specific user, but want to run a few tests as an anonymous user.
For example, the following will run withMockUser1 and withMockUser2 using <<test-method-withmockuser,@WithMockUser>> and anonymous as an anonymous user.
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@ExtendWith(SpringExtension.class)
@@ -324,7 +353,8 @@ public class WithUserClassLevelAuthenticationTests {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@ExtendWith(SpringExtension.class)
@@ -345,7 +375,7 @@ class WithUserClassLevelAuthenticationTests {
}
}
----
-====
+======
By default the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.
This is the equivalent of happening before JUnit's `@Before`.
@@ -370,8 +400,10 @@ That is exactly what `@WithUserDetails` does.
Assuming we have a `UserDetailsService` exposed as a bean, the following test will be invoked with an `Authentication` of type `UsernamePasswordAuthenticationToken` and a principal that is returned from the `UserDetailsService` with the username of "user".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -382,7 +414,8 @@ public void getMessageWithUserDetails() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -392,13 +425,15 @@ fun getMessageWithUserDetails() {
// ...
}
----
-====
+======
We can also customize the username used to lookup the user from our `UserDetailsService`.
For example, this test would be run with a principal that is returned from the `UserDetailsService` with the username of "customUsername".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -409,7 +444,8 @@ public void getMessageWithUserDetailsCustomUsername() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -419,13 +455,15 @@ fun getMessageWithUserDetailsCustomUsername() {
// ...
}
----
-====
+======
We can also provide an explicit bean name to look up the `UserDetailsService`.
For example, this test would look up the username of "customUsername" using the `UserDetailsService` with the bean name "myUserDetailsService".
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -436,7 +474,8 @@ public void getMessageWithUserDetailsServiceBeanName() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -446,7 +485,7 @@ fun getMessageWithUserDetailsServiceBeanName() {
// ...
}
----
-====
+======
Like `@WithMockUser` we can also place our annotation at the class level so that every test uses the same user.
However unlike `@WithMockUser`, `@WithUserDetails` requires the user to exist.
@@ -471,8 +510,10 @@ We will now see an option that allows the most flexibility.
We can create our own annotation that uses the `@WithSecurityContext` to create any `SecurityContext` we want.
For example, we might create an annotation named `@WithMockCustomUser` as shown below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@@ -485,22 +526,25 @@ public @interface WithMockCustomUser {
annotation class WithMockCustomUser(val username: String = "rob", val name: String = "Rob Winch")
----
-====
+======
You can see that `@WithMockCustomUser` is annotated with the `@WithSecurityContext` annotation.
This is what signals to Spring Security Test support that we intend to create a `SecurityContext` for the test.
The `@WithSecurityContext` annotation requires we specify a `SecurityContextFactory` that will create a new `SecurityContext` given our `@WithMockCustomUser` annotation.
You can find our `WithMockCustomUserSecurityContextFactory` implementation below:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public class WithMockCustomUserSecurityContextFactory
@@ -519,7 +563,8 @@ public class WithMockCustomUserSecurityContextFactory
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory<WithMockCustomUser> {
@@ -533,15 +578,17 @@ class WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory<With
}
}
----
-====
+======
We can now annotate a test class or a test method with our new annotation and Spring Security's `WithSecurityContextTestExecutionListener` will ensure that our `SecurityContext` is populated appropriately.
When creating your own `WithSecurityContextFactory` implementations, it is nice to know that they can be annotated with standard Spring annotations.
For example, the `WithUserDetailsSecurityContextFactory` uses the `@Autowired` annotation to acquire the `UserDetailsService`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
final class WithUserDetailsSecurityContextFactory
@@ -566,7 +613,8 @@ final class WithUserDetailsSecurityContextFactory
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
class WithUserDetailsSecurityContextFactory @Autowired constructor(private val userDetailsService: UserDetailsService) :
@@ -583,7 +631,7 @@ class WithUserDetailsSecurityContextFactory @Autowired constructor(private val u
}
}
----
-====
+======
By default the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.
This is the equivalent of happening before JUnit's `@Before`.
@@ -601,25 +649,30 @@ You can change this to happen during the `TestExecutionListener.beforeTestExecut
If you reuse the same user within your tests often, it is not ideal to have to repeatedly specify the attributes.
For example, if there are many tests related to an administrative user with the username "admin" and the roles `ROLE_USER` and `ROLE_ADMIN` you would have to write:
* Manually adding `SecurityContextPersistenceFilter` to the `MockMvc` instance may make sense when using `MockMvcBuilders.standaloneSetup`
====
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(get("/").with(user("user")))
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc.get("/") {
with(user("user"))
}
----
-====
+======
You can easily make customizations.
For example, the following will run as a user (which does not need to exist) with the username "admin", the password "pass", and the roles "ROLE_USER" and "ROLE_ADMIN".
If you have a custom `UserDetails` that you would like to use, you can easily specify that as well.
For example, the following will use the specified `UserDetails` (which does not need to exist) to run with a `UsernamePasswordAuthenticationToken` that has a principal of the specified `UserDetails`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(get("/").with(user(userDetails)))
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc.get("/") {
with(user(userDetails))
}
----
-====
+======
You can run as anonymous user using the following:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(get("/").with(anonymous()))
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc.get("/") {
with(anonymous())
}
----
-====
+======
This is especially useful if you are running with a default user and wish to process a few requests as an anonymous user.
If you want a custom `Authentication` (which does not need to exist) you can do so using the following:
We can also ensure to run as a specific user for every request by using ``MockMvcBuilders``'s default request.
For example, the following will run as a user (which does not need to exist) with the username "admin", the password "password", and the role "ROLE_ADMIN":
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc = MockMvcBuilders
@@ -154,7 +174,8 @@ mvc = MockMvcBuilders
.build();
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc = MockMvcBuilders
@@ -163,13 +184,15 @@ mvc = MockMvcBuilders
.apply<DefaultMockMvcBuilder>(springSecurity())
.build()
----
-====
+======
If you find you are using the same user in many of your tests, it is recommended to move the user to a method.
For example, you can specify the following in your own class named `CustomSecurityMockMvcRequestPostProcessors`:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
public static RequestPostProcessor rob() {
@@ -177,19 +200,22 @@ public static RequestPostProcessor rob() {
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
fun rob(): RequestPostProcessor {
return user("rob").roles("ADMIN")
}
----
-====
+======
Now you can perform a static import on `CustomSecurityMockMvcRequestPostProcessors` and use that within your tests:
== Running as a User in Spring MVC Test with Annotations
@@ -219,8 +246,10 @@ mvc.get("/") {
As an alternative to using a `RequestPostProcessor` to create your user, you can use annotations described in xref:servlet/test/method.adoc[Testing Method Security].
For example, the following will run the test with the user with username "user", password "password", and role "ROLE_USER":
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -232,7 +261,8 @@ mvc
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -243,12 +273,14 @@ fun requestProtectedUrlWithUser() {
// ...
}
----
-====
+======
Alternatively, the following will run the test with the user with username "user", password "password", and role "ROLE_ADMIN":
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
@Test
@@ -260,7 +292,8 @@ mvc
}
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
@Test
@@ -271,4 +304,4 @@ fun requestProtectedUrlWithUser() {
When testing any non-safe HTTP methods and using Spring Security's CSRF protection, you must be sure to include a valid CSRF Token in the request.
To specify a valid CSRF token as a request parameter use the CSRF xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] like so:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(post("/").with(csrf()))
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc.post("/") {
with(csrf())
}
----
-====
+======
If you like you can include CSRF token in the header instead:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(post("/").with(csrf().asHeader()))
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc.post("/") {
with(csrf().asHeader())
}
----
-====
+======
You can also test providing an invalid CSRF token using the following:
You can easily create a request to test a form based authentication using Spring Security's testing support.
For example, the following `formLogin` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] will submit a POST to "/login" with the username "user", the password "password", and a valid CSRF token:
-====
-.Java
+[tabs]
+======
+Java::
++
[source,java,role="primary"]
----
mvc
.perform(formLogin())
----
-.Kotlin
+Kotlin::
++
[source,kotlin,role="secondary"]
----
mvc
.perform(formLogin())
----
-====
+======
It is easy to customize the request.
For example, the following will submit a POST to "/auth" with the username "admin", the password "pass", and a valid CSRF token:
will attempt to use HTTP Basic to authenticate a user with the username "user" and the password "password" by ensuring the following header is populated on the HTTP Request: