Merge branch '5.8.x' into 6.0.x

Closes gh-13406
This commit is contained in:
Rob Winch
2023-06-18 21:33:58 -05:00
116 changed files with 4826 additions and 3206 deletions

View File

@@ -11,7 +11,10 @@ This will:
Often, you will want to also invalidate the session on logout.
To achieve this, you can add the `WebSessionServerLogoutHandler` to your logout configuration, like so:
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -28,7 +31,8 @@ SecurityWebFilterChain http(ServerHttpSecurity http) throws Exception {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -47,3 +51,4 @@ fun http(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
======

View File

@@ -5,8 +5,10 @@ Similar to xref:servlet/authentication/x509.adoc#servlet-x509[Servlet X.509 auth
The following example shows a reactive x509 security configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -20,7 +22,8 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -33,14 +36,16 @@ fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
In the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are 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 a user account with a name extracted by `principalExtractor` exists and that it is not locked, disabled, or expired.
The following example demonstrates how these defaults can be overridden:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -67,7 +72,8 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -89,7 +95,7 @@ fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
}
}
----
====
======
In the previous 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 is authenticated.

View File

@@ -6,8 +6,10 @@ By default, Spring Securitys authorization will require all requests to be au
The explicit configuration looks like:
.All Requests Require Authenticated User
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -22,7 +24,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -36,14 +39,16 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
We can configure Spring Security to have different rules by adding more rules in order of precedence.
.Multiple Authorize Requests Rules
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
import static org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.hasRole;
@@ -68,7 +73,8 @@ SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -88,7 +94,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
<1> There are multiple authorization rules specified.
Each rule is considered in the order they were declared.

View File

@@ -32,8 +32,10 @@ For earlier versions, please read about similar support with <<jc-enable-reactiv
For example, the following would enable Spring Security's `@PreAuthorize` annotation:
.Method Security Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager=true)
@@ -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:
.Method Security Annotation Usage
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public interface BankService {
@@ -63,7 +67,7 @@ public interface BankService {
Mono<Account> post(Account account, Double amount);
}
----
====
======
In this case `hasRole` refers to the method found in `SecurityExpressionRoot` that gets invoked by the SpEL evaluation engine.
@@ -71,8 +75,10 @@ In this case `hasRole` refers to the method found in `SecurityExpressionRoot` th
A bean like that might look something like this:
.Method Security Reactive Boolean Expression
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -80,20 +86,22 @@ public Function<Account, Mono<Boolean>> func() {
return (account) -> Mono.defer(() -> Mono.just(account.getId().equals(12)));
}
----
====
======
=== Customizing Authorization
Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support.
[[jc-reactive-method-security-custom-granted-authority-defaults]]
[[jc-reactive-method-security-custom-granted-authority-defaults]]
Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`.
You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:
.Custom MethodSecurityExpressionHandler
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -101,7 +109,7 @@ static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
----
====
======
[TIP]
====
@@ -124,8 +132,10 @@ If that authorization denies access, the value is not returned, and an `AccessDe
To recreate what adding `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` does by default, you would publish the following configuration:
.Full Pre-post Method Security Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -160,15 +170,17 @@ class MethodSecurityConfig {
}
}
----
====
======
Notice that Spring Security's method security is built using Spring AOP.
So, interceptors are invoked based on the order specified.
This can be customized by calling `setOrder` on the interceptor instances like so:
.Publish Custom Advisor
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -179,13 +191,15 @@ Advisor postFilterAuthorizationMethodInterceptor() {
return interceptor;
}
----
====
======
You may want to only support `@PreAuthorize` in your application, in which case you can do the following:
.Only @PreAuthorize Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -202,7 +216,7 @@ class MethodSecurityConfig {
}
}
----
====
======
Or, you may have a custom before-method `ReactiveAuthorizationManager` that you want to add to the list.
@@ -211,9 +225,11 @@ In this case, you will need to tell Spring Security both the `ReactiveAuthorizat
Thus, you can configure Spring Security to invoke your `ReactiveAuthorizationManager` in between `@PreAuthorize` and `@PostAuthorize` like so:
.Custom Before Advisor
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager=true)
@@ -230,7 +246,7 @@ class MethodSecurityConfig {
}
}
----
====
======
[TIP]
====
@@ -243,8 +259,10 @@ After-method authorization is generally concerned with analysing the return valu
For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so:
.@PostAuthorize example
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public interface BankService {
@@ -254,7 +272,7 @@ public interface BankService {
Mono<Account> readAccount(Long id);
}
----
====
======
You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated.
@@ -262,8 +280,10 @@ For example, if you have your own custom annotation, you can configure it like s
.Custom After Advisor
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager=true)
@@ -278,7 +298,7 @@ class MethodSecurityConfig {
}
}
----
====
======
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");
@@ -309,7 +331,8 @@ StepVerifier.create(messageByUsername)
.verifyComplete();
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
@@ -324,12 +347,14 @@ StepVerifier.create(messageByUsername)
.expectNext("Hi user")
.verifyComplete()
----
====
======
Where `this::findMessageByUsername` is defined as:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
Mono<String> findMessageByUsername(String username) {
@@ -337,19 +362,22 @@ Mono<String> findMessageByUsername(String username) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
fun findMessageByUsername(username: String): Mono<String> {
return Mono.just("Hi $username")
}
----
====
======
The following minimal method security configures method security in reactive applications:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -371,7 +399,8 @@ public class SecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -392,12 +421,14 @@ class SecurityConfig {
}
}
----
====
======
Consider the following class:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Component
@@ -409,7 +440,8 @@ public class HelloWorldMessageService {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Component
@@ -420,12 +452,14 @@ class HelloWorldMessageService {
}
}
----
====
======
Alternatively, the following class uses Kotlin coroutines:
====
.Kotlin
[tabs]
======
Kotlin::
+
[source,kotlin,role="primary"]
----
@Component
@@ -437,7 +471,7 @@ class HelloWorldMessageService {
}
}
----
====
======
Combined with our configuration above, `@PreAuthorize("hasRole('ADMIN')")` ensures that `findByMessage` is invoked only by a user with the `ADMIN` role.
@@ -447,8 +481,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"]
----
@Configuration
@@ -484,7 +520,8 @@ public class SecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -516,6 +553,6 @@ class SecurityConfig {
}
}
----
====
======
You can find a complete sample in {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method].

View File

@@ -14,8 +14,10 @@ A few sample applications demonstrate the code:
The following listing shows a minimal WebFlux Security configuration:
.Minimal WebFlux Security Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
-----
@Configuration
@@ -34,7 +36,8 @@ public class HelloWebfluxSecurityConfig {
}
-----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
-----
@Configuration
@@ -52,7 +55,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 login page and a default logout page, sets up security related HTTP headers, adds CSRF protection, and more.
@@ -61,8 +64,10 @@ This configuration provides form and HTTP basic authentication, sets up authoriz
The following page shows an explicit version of the minimal WebFlux Security configuration:
.Explicit WebFlux Security Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
-----
@Configuration
@@ -92,7 +97,8 @@ public class HelloWebfluxSecurityConfig {
}
-----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
-----
import org.springframework.security.config.web.server.invoke
@@ -123,7 +129,7 @@ class HelloWebfluxSecurityConfig {
}
}
-----
====
======
[NOTE]
Make sure that you import the `invoke` function in your Kotlin class, sometimes the IDE will not auto-import it causing compilation issues.
@@ -140,8 +146,10 @@ You can configure multiple `SecurityWebFilterChain` instances to separate config
For example, you can isolate configuration for URLs that start with `/api`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -179,7 +187,8 @@ static class MultiSecurityHttpConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
import org.springframework.security.config.web.server.invoke
@@ -219,13 +228,13 @@ open class MultiSecurityHttpConfig {
}
}
----
======
<1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first
<2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`
<3> Specify the authentication mechanisms that will be used for `/api/**` endpoints
<4> Create another instance of `SecurityWebFilterChain` with lower precedence to match all other URLs
<5> Specify the authentication mechanisms that will be used for the rest of the application
====
Spring Security selects one `SecurityWebFilterChain` `@Bean` for each request.
It matches the requests in order by the `securityMatcher` definition.

View File

@@ -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:
.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, you can disable CSRF protection if it xref:features/exploits/csrf.adoc#
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 @@ As of 6.0, the default implementation is `XorServerCsrfTokenRequestAttributeHand
If you wish to disable BREACH protection of the `CsrfToken` and revert to the 5.8 default, you can configure `ServerCsrfTokenRequestAttributeHandler` using the following Java configuration:
.Disable 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<
The following example places 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 @@ To post an HTML form, the CSRF token must be included in the form as a hidden in
The following example shows what the rendered HTML might look like:
.CSRF Token HTML
====
[source,html]
----
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
----
====
Next, we 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 following Thymeleaf sample 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 following Thymeleaf sample assumes that you <<webflux-csrf-include-subscribe
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 contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header.
If you use jQuery, you could read the meta tags with the following code:
.AJAX send CSRF Token
====
[source,javascript]
----
$(function () {
@@ -289,13 +294,11 @@ $(function () {
});
});
----
====
The following sample assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`.
The following example does this with Thymeleaf:
.CSRF meta tag JSP
====
[source,html]
----
<html>
@@ -307,7 +310,6 @@ The following example does this with Thymeleaf:
</head>
<!-- ... -->
----
====
[[webflux-csrf-considerations]]
== CSRF Considerations
@@ -339,8 +341,10 @@ For example, the following Java Configuration logs out when the `/logout` URL is
// 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, you can do so 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
An example with Thymeleaf is shown below:
.CSRF Token in Action
====
[source,html]
----
<form method="post"
th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"
enctype="multipart/form-data">
----
====
[[webflux-csrf-considerations-override-method]]
=== HiddenHttpMethodFilter

View File

@@ -16,8 +16,10 @@ For example, assume that you want the defaults but you wish to specify `SAMEORIG
You can do so with the following configuration:
.Customize Default Security Headers
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -33,7 +35,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -48,14 +51,16 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
If you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults:
.Disable HTTP Security Response Headers
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -67,7 +72,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -80,7 +86,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-cache-control]]
== Cache Control
@@ -96,8 +102,10 @@ You can find details on how to do so in the https://docs.spring.io/spring/docs/5
If necessary, you can also disable Spring Security's cache control HTTP response headers.
.Cache Control Disabled
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -111,7 +119,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -126,7 +135,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-content-type-options]]
@@ -135,8 +144,10 @@ By default, Spring Security includes xref:features/exploits/headers.adoc#headers
However, you can disable it:
.Content Type Options Disabled
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -150,7 +161,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -165,7 +177,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-hsts]]
== HTTP Strict Transport Security (HSTS)
@@ -174,8 +186,10 @@ However, you can customize the results explicitly.
For example, the following example explicitly provides HSTS:
.Strict Transport Security
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -193,7 +207,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -210,7 +225,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-frame-options]]
== X-Frame-Options
@@ -219,8 +234,10 @@ By default, Spring Security disables rendering within an iframe by using xref:fe
You can customize frame options to use the same origin:
.X-Frame-Options: SAMEORIGIN
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -236,7 +253,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -251,7 +269,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-xss-protection]]
== X-XSS-Protection
@@ -259,8 +277,10 @@ By default, Spring Security instructs browsers to disable the XSS Auditor by usi
You can disable the `X-XSS-Protection` header entirely:
.X-XSS-Protection Customization
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -274,7 +294,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -289,13 +310,15 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
You can also change the header value:
.X-XSS-Protection Explicit header value
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -309,7 +332,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -324,7 +348,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-csp]]
== Content Security Policy (CSP)
@@ -334,18 +358,18 @@ The web application author must declare the security policies to enforce and/or
For example, consider the following security policy:
.Content Security Policy Example
====
[source,http]
----
Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
----
====
Given the preceding policy, you can enable the CSP header:
.Content Security Policy
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -361,7 +385,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -376,13 +401,15 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
To enable the CSP `report-only` header, provide the following configuration:
.Content Security Policy Report Only
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -399,7 +426,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -415,7 +443,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-referrer]]
== Referrer Policy
@@ -424,8 +452,10 @@ By default, Spring Security does not add xref:features/exploits/headers.adoc#hea
You can enable the Referrer Policy header using configuration as shown below:
.Referrer Policy Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -441,7 +471,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -456,7 +487,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-feature]]
@@ -466,18 +497,18 @@ By default, Spring Security does not add xref:features/exploits/headers.adoc#hea
Consider the following `Feature-Policy` header:
.Feature-Policy Example
====
[source]
----
Feature-Policy: geolocation 'self'
----
====
You can enable the preceding Feature Policy header:
.Feature-Policy Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -491,7 +522,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -504,7 +536,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-permissions]]
@@ -514,18 +546,18 @@ By default, Spring Security does not add xref:features/exploits/headers.adoc#hea
Consider the following `Permissions-Policy` header:
.Permissions-Policy Example
====
[source]
----
Permissions-Policy: geolocation=(self)
----
====
You can enable the preceding Permissions Policy header:
.Permissions-Policy Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -541,7 +573,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -556,7 +589,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======
[[webflux-headers-clear-site-data]]
@@ -566,17 +599,17 @@ By default, Spring Security does not add xref:features/exploits/headers.adoc#hea
Consider the following `Clear-Site-Data` header:
.Clear-Site-Data Example
====
----
Clear-Site-Data: "cache", "cookies"
----
====
You can send the `Clear-Site-Data` header on logout:
.Clear-Site-Data Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -594,7 +627,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -612,4 +646,4 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
}
}
----
====
======

View File

@@ -13,8 +13,10 @@ If a client makes a request using HTTP rather than HTTPS, you can configure Spri
The following Java configuration redirects any HTTP requests to HTTPS:
.Redirect to HTTPS
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -26,7 +28,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -37,15 +40,17 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
You can wrap the configuration can be wrapped around an `if` statement to be turned on only in production.
Alternatively, you can enable it by looking for a property about the request that happens only in production.
For example, if the production environment adds a header named `X-Forwarded-Proto`, you should use the following Java Configuration:
.Redirect to HTTPS when X-Forwarded
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -59,7 +64,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -74,7 +80,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
[[webflux-hsts]]
== Strict Transport Security

View File

@@ -14,8 +14,10 @@ For your convenience, you can download a minimal Reactive Spring Boot + Spring S
You can add Spring Security to your Spring Boot project by adding `spring-boot-starter-security`.
====
.Maven
[tabs]
======
Maven::
+
[source,xml,role="primary"]
----
<dependency>
@@ -24,12 +26,13 @@ You can add Spring Security to your Spring Boot project by adding `spring-boot-s
</dependency>
----
.Gradle
Gradle::
+
[source,groovy,role="secondary"]
----
implementation 'org.springframework.boot:spring-boot-starter-security'
----
====
======
[[servlet-hello-starting]]
@@ -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
...
----
====
======
[[authenticating]]
== Authenticating

View File

@@ -10,8 +10,10 @@ The easiest way to ensure that CORS is handled first is to use the `CorsWebFilte
Users can integrate the `CorsWebFilter` with Spring Security by providing a `CorsConfigurationSource`.
For example, the following will integrate CORS support within Spring Security:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -25,7 +27,8 @@ CorsConfigurationSource corsConfigurationSource() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -38,12 +41,14 @@ fun corsConfigurationSource(): CorsConfigurationSource {
return source
}
----
====
======
The following will disable the CORS integration within Spring Security:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -55,7 +60,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -68,4 +74,4 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======

View File

@@ -17,8 +17,10 @@ When an `ObservationRegistry` bean is present, Spring Security creates traces fo
For example, consider a simple Boot application:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@SpringBootApplication
@@ -45,7 +47,8 @@ public class MyApplication {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@SpringBootApplication
@@ -72,20 +75,17 @@ class MyApplication {
}
}
----
====
======
And a corresponding request:
====
[source,bash]
----
?> http -a user:password :8080
----
====
Will produce the following output (indentation added for clarity):
====
[source,bash]
----
START - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@5dfdb78', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.00191856, duration(nanos)=1918560.0, startTimeNanos=101177265022745}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@121549e0']
@@ -101,15 +101,16 @@ START - name='http.server.requests', contextualName='null', error='null', lowCar
STOP - name='spring.security.http.chains', contextualName='spring.security.http.chains.after', error='null', lowCardinalityKeyValues=[chain.size='14', filter.section='after'], highCardinalityKeyValues=[request.line='/'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@40b25623', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.001693146, duration(nanos)=1693146.0, startTimeNanos=101178044824275}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@3b6cec2']
STOP - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@5dfdb78', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.784320641, duration(nanos)=7.84320641E8, startTimeNanos=101177265022745}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@121549e0']
----
====
[[webflux-observability-tracing-manual-configuration]]
=== Manual Configuration
For a non-Spring Boot application, or to override the existing Boot configuration, you can publish your own `ObservationRegistry` and Spring Security will still pick it up.
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@SpringBootApplication
@@ -138,7 +139,8 @@ public class MyApplication {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@SpringBootApplication
@@ -167,7 +169,8 @@ class MyApplication {
}
----
.Xml
Xml::
+
[source,kotlin,role="secondary"]
----
<sec:http auto-config="true" observation-registry-ref="ref">
@@ -176,7 +179,7 @@ class MyApplication {
<!-- define and configure ObservationRegistry bean -->
----
====
======
[[webflux-observability-tracing-disable]]
=== Disabling Observability
@@ -186,8 +189,10 @@ However, this may turn off observations for more than just Spring Security.
Instead, you can alter the provided `ObservationRegistry` with an `ObservationPredicate` like the following:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -197,7 +202,8 @@ ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations(
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -206,7 +212,7 @@ fun noSpringSecurityObservations(): ObservationRegistryCustomizer<ObservationReg
(registry: ObservationRegistry) -> registry.observationConfig().observationPredicate(predicate)
}
----
====
======
[TIP]
There is no facility for disabling observations with XML support.

View File

@@ -14,8 +14,10 @@ The following example shows a minimal RSocket Security configuration:
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
Doing so connects our `PayloadSocketAcceptorInterceptor` with the RSocket infrastructure.
In a Spring Boot application, you can do this automatically by using `RSocketSecurityAutoConfiguration` with the following code:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -71,7 +76,8 @@ RSocketServerCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInte
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -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 by using `AuthenticationPayloadExchangeConverter`, which is automatically setup by using the `simpleAuthentication` portion of the DSL.
The following example shows an explicit configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -140,7 +148,8 @@ PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -154,30 +163,35 @@ open fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInte
return rsocket.build()
}
----
====
======
The RSocket sender can send credentials by using `SimpleAuthenticationEncoder`, which you can add to Spring's `RSocketStrategies`.
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
RSocketStrategies.Builder strategies = ...;
strategies.encoder(new SimpleAuthenticationEncoder());
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
var strategies: RSocketStrategies.Builder = ...
strategies.encoder(SimpleAuthenticationEncoder())
----
====
======
You can then use it to send a username and password to the receiver in the setup:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
MimeType authenticationMimeType =
@@ -189,7 +203,8 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
.connectTcp(host, port);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val authenticationMimeType: MimeType =
@@ -200,12 +215,14 @@ val requester: Mono<RSocketRequester> = RSocketRequester.builder()
.rsocketStrategies(strategies.build())
.connectTcp(host, port)
----
====
======
Alternatively or additionally, a username and password can be sent in a request.
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
Mono<RSocketRequester> requester;
@@ -220,7 +237,8 @@ public Mono<AirportLocation> findRadar(String code) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
import org.springframework.messaging.rsocket.retrieveMono
@@ -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 that the JWT
The RSocket receiver can decode the credentials by using `BearerPayloadExchangeConverter`, which is automatically setup by using the `jwt` portion of the DSL.
The following listing shows an example configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -266,7 +286,8 @@ PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -280,13 +301,15 @@ fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorIntercept
return rsocket.build()
}
----
====
======
The configuration above relies on the existence of a `ReactiveJwtDecoder` `@Bean` being present.
An example of creating one from the issuer can be found below:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -296,7 +319,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -305,13 +329,15 @@ fun jwtDecoder(): ReactiveJwtDecoder {
.fromIssuerLocation("https://example.com/auth/realms/demo")
}
----
====
======
The RSocket sender does not need to do anything special to send the token, because the value is a simple `String`.
The following example sends the token at setup time:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
MimeType authenticationMimeType =
@@ -322,7 +348,8 @@ Mono<RSocketRequester> requester = RSocketRequester.builder()
.connectTcp(host, port);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val authenticationMimeType: MimeType =
@@ -333,12 +360,14 @@ val requester = RSocketRequester.builder()
.setupMetadata(token, authenticationMimeType)
.connectTcp(host, port)
----
====
======
Alternatively or additionally, you can send the token in a request:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
MimeType authenticationMimeType =
@@ -355,7 +384,8 @@ public Mono<AirportLocation> findRadar(String code) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val authenticationMimeType: MimeType =
@@ -371,7 +401,7 @@ open fun findRadar(code: String): Mono<AirportLocation> {
}
}
----
====
======
[[rsocket-authorization]]
== RSocket Authorization
@@ -380,8 +410,10 @@ RSocket authorization is performed with `AuthorizationPayloadInterceptor`, which
You can use the DSL to set up authorization rules based upon the `PayloadExchange`.
The following listing shows an example configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
rsocket
@@ -397,7 +429,9 @@ rsocket
.anyExchange().permitAll() // <6>
);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
rsocket
@@ -413,6 +447,7 @@ rsocket
.anyExchange().permitAll()
} // <6>
----
======
<1> Setting up a connection requires the `ROLE_SETUP` authority.
<2> If the route is `fetch.profile.me`, authorization only requires the user to be authenticated.
<3> In this rule, we set up a custom matcher, where authorization requires the user to have the `ROLE_CUSTOM` authority.
@@ -424,7 +459,6 @@ A request is where the metadata is included.
It would not include additional payloads.
<6> This rule ensures that any exchange that does not already have a rule is allowed for anyone.
In this example, it means that payloads that have no metadata also have no authorization rules.
====
Note that authorization rules are performed in order.
Only the first authorization rule that matches is invoked.

View File

@@ -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"]
----
@Configuration
@@ -155,7 +157,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -196,7 +199,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.
@@ -221,8 +224,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.
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
@@ -232,7 +237,8 @@ private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomi
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
@@ -245,7 +251,7 @@ private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationReques
}
}
----
====
======
=== Storing the Authorization Request
@@ -260,8 +266,10 @@ The default implementation of `ServerAuthorizationRequestRepository` is `WebSess
If you have a custom implementation of `ServerAuthorizationRequestRepository`, you may configure it as shown in the following example:
.ServerAuthorizationRequestRepository Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -280,7 +288,8 @@ public class OAuth2ClientSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -299,7 +308,7 @@ class OAuth2ClientSecurityConfig {
}
}
----
====
======
=== Requesting an Access Token
@@ -335,8 +344,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveAuthorizationCodeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, youll need to configure it as shown in the following example:
.Access Token Response Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -363,7 +374,8 @@ public class OAuth2ClientSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -389,7 +401,7 @@ class OAuth2ClientSecurityConfig {
}
}
----
====
======
[[oauth2Client-refresh-token-grant]]
@@ -433,8 +445,10 @@ Alternatively, if your requirements are more advanced, you can take full control
Whether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, youll need to configure it as shown in the following example:
.Access Token Response Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
// Customize
@@ -451,7 +465,8 @@ ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
// Customize
@@ -466,7 +481,7 @@ val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveO
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
----
====
======
[NOTE]
`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,
@@ -516,8 +531,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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
// Customize
@@ -533,7 +550,8 @@ ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
// Customize
@@ -547,7 +565,7 @@ val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveO
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
----
====
======
[NOTE]
`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`,
@@ -576,8 +594,10 @@ spring:
...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -599,7 +619,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -615,12 +636,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
====
======
You may obtain the `OAuth2AccessToken` as follows:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Controller
@@ -644,7 +667,8 @@ public class OAuth2ClientController {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class OAuth2ClientController {
@@ -666,7 +690,7 @@ class OAuth2ClientController {
}
}
----
====
======
[NOTE]
`ServerWebExchange` is an OPTIONAL attribute.
@@ -713,8 +737,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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
// Customize
@@ -731,7 +757,8 @@ ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val passwordTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
@@ -745,7 +772,7 @@ val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.bui
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
----
====
======
[NOTE]
`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordReactiveOAuth2AuthorizedClientProvider`,
@@ -774,8 +801,10 @@ spring:
...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -819,7 +848,9 @@ private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttri
};
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -858,12 +889,14 @@ private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<Mut
}
}
----
====
======
You may obtain the `OAuth2AccessToken` as follows:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Controller
@@ -887,7 +920,8 @@ public class OAuth2ClientController {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Controller
@@ -909,7 +943,7 @@ class OAuth2ClientController {
}
}
----
====
======
[NOTE]
`ServerWebExchange` is an OPTIONAL attribute.
@@ -955,8 +989,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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
// Customize
@@ -975,7 +1011,8 @@ ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
// Customize
@@ -992,7 +1029,7 @@ val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.bui
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
----
====
======
=== Using the Access Token
@@ -1017,8 +1054,10 @@ spring:
...and the `OAuth2AuthorizedClientManager` `@Bean`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -1043,7 +1082,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -1060,12 +1100,14 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
====
======
You may obtain the `OAuth2AccessToken` as follows:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@RestController
@@ -1087,7 +1129,8 @@ public class OAuth2ResourceServerController {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class OAuth2ResourceServerController {
@@ -1106,7 +1149,7 @@ class OAuth2ResourceServerController {
}
}
----
====
======
[NOTE]
`JwtBearerReactiveOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.

View File

@@ -8,8 +8,10 @@
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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -72,7 +77,8 @@ WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManage
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -83,7 +89,7 @@ fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): W
.build()
}
----
====
======
=== Providing the Authorized Client
@@ -91,8 +97,10 @@ The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client t
The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/")
@@ -110,7 +118,8 @@ public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2Author
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/")
@@ -127,14 +136,16 @@ fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2Auth
.thenReturn("index")
}
----
====
======
<1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/")
@@ -152,7 +163,8 @@ public Mono<String> index() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/")
@@ -169,7 +181,7 @@ fun index(): Mono<String> {
.thenReturn("index")
}
----
====
======
<1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
@@ -181,8 +193,10 @@ If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authe
The following code shows the specific configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -196,7 +210,8 @@ WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManage
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -208,7 +223,7 @@ fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): W
.build()
}
----
====
======
[WARNING]
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
@@ -217,8 +232,10 @@ Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a
The following code shows the specific configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -232,7 +249,8 @@ WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManage
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -244,7 +262,7 @@ fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): W
.build()
}
----
====
======
[WARNING]
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.

View File

@@ -36,8 +36,10 @@ spring:
The following example shows how to configure `WebClientReactiveAuthorizationCodeTokenResponseClient`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
@@ -59,7 +61,8 @@ tokenResponseClient.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val jwkResolver: Function<ClientRegistration, JWK> =
@@ -81,7 +84,7 @@ tokenResponseClient.addParametersConverter(
NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)
----
====
======
=== Authenticate using `client_secret_jwt`
@@ -105,8 +108,10 @@ spring:
The following example shows how to configure `WebClientReactiveClientCredentialsTokenResponseClient`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
@@ -127,7 +132,8 @@ tokenResponseClient.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->
@@ -148,14 +154,16 @@ tokenResponseClient.addParametersConverter(
NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)
----
====
======
=== Customizing the JWT assertion
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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
Function<ClientRegistration, JWK> jwkResolver = ...
@@ -168,7 +176,8 @@ converter.setJwtClientAssertionCustomizer((context) -> {
});
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
val jwkResolver = ...
@@ -180,4 +189,4 @@ converter.setJwtClientAssertionCustomizer { context ->
context.claims.claim("custom-claim", "claim-value")
}
----
====
======

View File

@@ -69,20 +69,23 @@ A `ClientRegistration` can be initially configured using discovery of an OpenID
`ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as can be seen in the following example:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
ClientRegistration clientRegistration =
ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
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`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -337,7 +351,8 @@ private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttri
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -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(
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -426,4 +444,4 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
====
======

View File

@@ -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"]
----
@Configuration
@@ -48,7 +50,8 @@ public class OAuth2ClientSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -71,14 +74,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
@@ -103,7 +108,8 @@ public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -122,4 +128,4 @@ fun authorizedClientManager(
return authorizedClientManager
}
----
====
======

View File

@@ -23,8 +23,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"]
----
@Configuration
@@ -53,7 +55,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -82,7 +85,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
The following sections go into more detail on each of the configuration options available:
@@ -119,8 +122,10 @@ To override the default login page, configure the `exceptionHandling().authentic
The following listing shows an example:
.OAuth2 Login Page Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary",subs="-attributes"]
----
@Configuration
@@ -153,7 +158,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",subs="-attributes"]
----
@Configuration
@@ -187,7 +193,7 @@ class OAuth2LoginSecurityConfig {
...
}
----
====
======
[IMPORTANT]
You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page.
@@ -220,8 +226,10 @@ The default Authorization Response redirection endpoint is `+/login/oauth2/code/
If you would like to customize the Authorization Response redirection endpoint, configure it as shown in the following example:
.Redirection Endpoint Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary",subs="-attributes"]
----
@Configuration
@@ -240,7 +248,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",subs="-attributes"]
----
@Configuration
@@ -259,7 +268,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[IMPORTANT]
====
@@ -267,7 +276,10 @@ You also need to ensure the `ClientRegistration.redirectUri` matches the custom
The following listing shows an example:
.Java
[tabs]
======
Java::
+
[source,java,role="primary",subs="-attributes"]
----
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
@@ -277,7 +289,8 @@ return CommonOAuth2Provider.GOOGLE.getBuilder("google")
.build();
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",subs="-attributes"]
----
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
@@ -286,6 +299,7 @@ return CommonOAuth2Provider.GOOGLE.getBuilder("google")
.redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
.build()
----
======
====
@@ -322,8 +336,10 @@ The `GrantedAuthoritiesMapper` is given a list of granted authorities which cont
Register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example:
.Granted Authorities Mapper Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -371,7 +387,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -408,7 +425,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[[webflux-oauth2-login-advanced-map-authorities-reactiveoauth2userservice]]
==== Delegation-based strategy with ReactiveOAuth2UserService
@@ -420,8 +437,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:
.ReactiveOAuth2UserService Configuration
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -462,7 +481,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -501,7 +521,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[[webflux-oauth2-login-advanced-oauth2-user-service]]
@@ -518,8 +538,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"]
----
@Configuration
@@ -542,7 +564,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -564,7 +587,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[[webflux-oauth2-login-advanced-oidc-user-service]]
@@ -578,8 +601,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"]
----
@Configuration
@@ -602,7 +627,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -624,7 +650,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[[webflux-oauth2-login-advanced-idtoken-verify]]
@@ -641,8 +667,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
@@ -653,7 +681,8 @@ public ReactiveJwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -663,7 +692,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.
@@ -699,8 +728,10 @@ spring:
...and the `OidcClientInitiatedServerLogoutSuccessHandler`, which implements RP-Initiated Logout, may be configured as follows:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary",subs="-attributes"]
----
@Configuration
@@ -737,7 +768,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",subs="-attributes"]
----
@Configuration
@@ -772,7 +804,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.

View File

@@ -252,8 +252,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
@@ -283,7 +285,8 @@ public class OAuth2LoginConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",attrs="-attributes"]
----
@Configuration
@@ -312,7 +315,7 @@ class OAuth2LoginConfig {
}
}
----
====
======
[[webflux-oauth2-login-register-securitywebfilterchain-bean]]
@@ -321,8 +324,10 @@ 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"]
----
@Configuration
@@ -342,7 +347,8 @@ public class OAuth2LoginSecurityConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -362,7 +368,7 @@ class OAuth2LoginSecurityConfig {
}
}
----
====
======
[[webflux-oauth2-login-completely-override-autoconfiguration]]
@@ -371,8 +377,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"]
----
@Configuration
@@ -414,7 +422,8 @@ public class OAuth2LoginConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary",attrs="-attributes"]
----
@Configuration
@@ -456,7 +465,7 @@ class OAuth2LoginConfig {
}
}
----
====
======
[[webflux-oauth2-login-javaconfig-wo-boot]]
@@ -465,8 +474,10 @@ 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"]
----
@Configuration
@@ -510,7 +521,8 @@ public class OAuth2LoginConfig {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@@ -556,4 +568,4 @@ class OAuth2LoginConfig {
}
}
----
====
======

View File

@@ -10,8 +10,10 @@ For example, you may have a need to read the bearer token from a custom header.
To do so, you can wire an instance of `ServerBearerTokenAuthenticationConverter` into the DSL:
.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 have a bearer token, you can pass that to downstream services.
This is possible with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServerBearerExchangeFilterFunction.html[ServerBearerExchangeFilterFunction]`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -52,7 +57,8 @@ public WebClient rest() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -62,13 +68,15 @@ fun rest(): WebClient {
.build()
}
----
====
======
When the `WebClient` shown in the preceding example performs requests, Spring Security looks up the current `Authentication` and extract any `{security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]` credential.
Then, it propagates that token in the `Authorization` header -- for example:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
this.rest.get()
@@ -77,7 +85,8 @@ this.rest.get()
.bodyToMono(String.class)
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
this.rest.get()
@@ -85,14 +94,16 @@ this.rest.get()
.retrieve()
.bodyToMono<String>()
----
====
======
The prececing example invokes the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.
In places where you need to override this behavior, you can supply the header yourself:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
this.rest.get()
@@ -102,7 +113,8 @@ this.rest.get()
.bodyToMono(String.class)
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
rest.get()
@@ -111,7 +123,7 @@ rest.get()
.retrieve()
.bodyToMono<String>()
----
====
======
In this case, the filter falls back and forwards the request onto the rest of the web filter chain.

View File

@@ -16,7 +16,6 @@ First, include the needed dependencies. Second, indicate the location of the aut
In a Spring Boot application, you need to specify which authorization server to use:
====
[source,yml]
----
spring:
@@ -26,7 +25,6 @@ spring:
jwt:
issuer-uri: https://idp.example.com/issuer
----
====
Where `https://idp.example.com/issuer` is the value contained in the `iss` claim for JWT tokens that the authorization server issues.
This resource server uses this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs.
@@ -58,13 +56,11 @@ If the authorization server is down when Resource Server queries it (given appro
Once the application is started up, Resource Server tries to process any request that contains an `Authorization: Bearer` header:
====
[source,html]
----
GET / HTTP/1.1
Authorization: Bearer some-token-value # Resource Server will process this
----
====
So long as this scheme is indicated, Resource Server tries to process the request according to the Bearer Token specification.
@@ -91,7 +87,6 @@ From here, consider jumping to:
If the authorization server does not support any configuration endpoints, or if Resource Server must be able to start up independently from the authorization server, you can supply `jwk-set-uri` as well:
====
[source,yaml]
----
spring:
@@ -102,7 +97,6 @@ spring:
issuer-uri: https://idp.example.com
jwk-set-uri: https://idp.example.com/.well-known/jwks.json
----
====
[NOTE]
====
@@ -125,8 +119,10 @@ Spring Boot generates two `@Bean` objects on Resource Server's behalf.
The first bean is a `SecurityWebFilterChain` that configures the application as a resource server. When including `spring-security-oauth2-jose`, this `SecurityWebFilterChain` looks like:
.Resource Server SecurityWebFilterChain
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -140,7 +136,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -155,15 +152,17 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
If the application does not expose a `SecurityWebFilterChain` bean, Spring Boot exposes the default one (shown in the preceding listing).
To replace it, expose the `@Bean` within the application:
.Replacing SecurityWebFilterChain
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -180,7 +179,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -196,7 +196,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
The preceding configuration requires the scope of `message:read` for any URL that starts with `/messages/`.
@@ -205,8 +205,10 @@ Methods on the `oauth2ResourceServer` DSL also override or replace auto configur
For example, the second `@Bean` Spring Boot creates is a `ReactiveJwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:
.ReactiveJwtDecoder
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -215,7 +217,8 @@ public ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -223,7 +226,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return ReactiveJwtDecoders.fromIssuerLocation(issuerUri)
}
----
====
======
[NOTE]
====
@@ -238,8 +241,10 @@ Its configuration can be overridden by using `jwkSetUri()` or replaced by using
You can configure an authorization server's JWK Set URI <<webflux-oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or supply it in the DSL:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -257,7 +262,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -274,7 +280,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
Using `jwkSetUri()` takes precedence over any configuration property.
@@ -283,8 +289,10 @@ Using `jwkSetUri()` takes precedence over any configuration property.
`decoder()` is more powerful than `jwkSetUri()`, because it completely replaces any Spring Boot auto-configuration of `JwtDecoder`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -302,7 +310,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -319,7 +328,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
This is handy when you need deeper configuration, such as <<webflux-oauth2resourceserver-jwt-validation,validation>>.
@@ -328,8 +337,10 @@ This is handy when you need deeper configuration, such as <<webflux-oauth2resour
Alternately, exposing a `ReactiveJwtDecoder` `@Bean` has the same effect as `decoder()`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -338,7 +349,8 @@ public ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -346,7 +358,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return ReactiveJwtDecoders.fromIssuerLocation(issuerUri)
}
----
====
======
[[webflux-oauth2resourceserver-jwt-decoder-algorithm]]
== Configuring Trusted Algorithms
@@ -360,7 +372,6 @@ You can customize this behavior with <<webflux-oauth2resourceserver-jwt-boot-alg
The simplest way to set the algorithm is as a property:
====
[source,yaml]
----
spring:
@@ -371,15 +382,16 @@ spring:
jws-algorithm: RS512
jwk-set-uri: https://idp.example.org/.well-known/jwks.json
----
====
[[webflux-oauth2resourceserver-jwt-decoder-builder]]
=== Customizing Trusted Algorithms by Using a Builder
For greater power, though, we can use a builder that ships with `NimbusReactiveJwtDecoder`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -389,7 +401,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -398,12 +411,14 @@ fun jwtDecoder(): ReactiveJwtDecoder {
.jwsAlgorithm(RS512).build()
}
----
====
======
Calling `jwsAlgorithm` more than once configures `NimbusReactiveJwtDecoder` to trust more than one algorithm:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -413,7 +428,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -422,12 +438,14 @@ fun jwtDecoder(): ReactiveJwtDecoder {
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()
}
----
====
======
Alternately, you can call `jwsAlgorithms`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -440,7 +458,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -453,7 +472,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
.build()
}
----
====
======
[[webflux-oauth2resourceserver-jwt-decoder-public-key]]
=== Trusting a Single Asymmetric Key
@@ -466,7 +485,6 @@ The public key can be provided with <<webflux-oauth2resourceserver-jwt-decoder-p
You can specify a key with Spring Boot:
====
[source,yaml]
----
spring:
@@ -476,13 +494,14 @@ spring:
jwt:
public-key-location: classpath:my-key.pub
----
====
Alternately, to allow for a more sophisticated lookup, you can post-process the `RsaKeyConversionServicePostProcessor`:
.BeanFactoryPostProcessor
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -493,7 +512,8 @@ BeanFactoryPostProcessor conversionServiceCustomizer() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -504,42 +524,45 @@ fun conversionServiceCustomizer(): BeanFactoryPostProcessor {
}
}
----
====
======
Specify your key's location:
====
[source,yaml]
----
key.location: hfds://my-key.pub
----
====
Then autowire the value:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Value("${key.location}")
RSAPublicKey key;
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Value("\${key.location}")
val key: RSAPublicKey? = null
----
====
======
[[webflux-oauth2resourceserver-jwt-decoder-public-key-builder]]
==== Using a Builder
To wire an `RSAPublicKey` directly, use the appropriate `NimbusReactiveJwtDecoder` builder:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -548,7 +571,8 @@ public ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -556,7 +580,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return NimbusReactiveJwtDecoder.withPublicKey(key).build()
}
----
====
======
[[webflux-oauth2resourceserver-jwt-decoder-secret-key]]
=== Trusting a Single Symmetric Key
@@ -564,8 +588,10 @@ fun jwtDecoder(): ReactiveJwtDecoder {
You can also use a single symmetric key.
You can load in your `SecretKey` and use the appropriate `NimbusReactiveJwtDecoder` builder:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -574,7 +600,8 @@ public ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -582,26 +609,26 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return NimbusReactiveJwtDecoder.withSecretKey(this.key).build()
}
----
====
======
[[webflux-oauth2resourceserver-jwt-authorization]]
=== Configuring Authorization
A JWT that is issued from an OAuth 2.0 Authorization Server typically has either a `scope` or an `scp` attribute, indicating the scopes (or authorities) it has been granted -- for example:
====
[source,json]
----
{ ..., "scope" : "messages contacts"}
----
====
When this is the case, Resource Server tries to coerce these scopes into a list of granted authorities, prefixing each scope with the string, `SCOPE_`.
This means that, to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -617,7 +644,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -634,25 +662,28 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
You can do something similar with method security:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@PreAuthorize("hasAuthority('SCOPE_messages')")
public Flux<Message> getMessages(...) {}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasAuthority('SCOPE_messages')")
fun getMessages(): Flux<Message> { }
----
====
======
[[webflux-oauth2resourceserver-jwt-authorization-extraction]]
==== Extracting Authorities Manually
@@ -663,8 +694,10 @@ At other times, the resource server may need to adapt the attribute or a composi
To this end, the DSL exposes `jwtAuthenticationConverter()`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -690,7 +723,8 @@ Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor()
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -713,15 +747,17 @@ fun grantedAuthoritiesExtractor(): Converter<Jwt, Mono<AbstractAuthenticationTok
return ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter)
}
----
====
======
`jwtAuthenticationConverter()` 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 the following `GrantedAuthoritiesExtractor`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
static class GrantedAuthoritiesExtractor
@@ -739,7 +775,8 @@ static class GrantedAuthoritiesExtractor
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
internal class GrantedAuthoritiesExtractor : Converter<Jwt, Collection<GrantedAuthority>> {
@@ -752,12 +789,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>> {
@@ -767,7 +806,8 @@ static class CustomAuthenticationConverter implements Converter<Jwt, Mono<Abstra
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
internal class CustomAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
@@ -776,7 +816,7 @@ internal class CustomAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthe
}
}
----
====
======
[[webflux-oauth2resourceserver-jwt-validation]]
=== Configuring Validation
@@ -795,8 +835,10 @@ This can cause some implementation heartburn, as the number of collaborating ser
Resource Server uses `JwtTimestampValidator` to verify a token's validity window, and you can configure it with a `clockSkew` to alleviate the clock drift problem:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -814,7 +856,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -827,7 +870,7 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return jwtDecoder
}
----
====
======
[NOTE]
====
@@ -839,8 +882,10 @@ By default, Resource Server configures a clock skew of 60 seconds.
You can Add a check for the `aud` claim with the `OAuth2TokenValidator` API:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
@@ -856,7 +901,8 @@ public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class AudienceValidator : OAuth2TokenValidator<Jwt> {
@@ -870,12 +916,14 @@ class AudienceValidator : OAuth2TokenValidator<Jwt> {
}
}
----
====
======
Then, to add into a resource server, you can specifying the `ReactiveJwtDecoder` instance:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -893,7 +941,8 @@ ReactiveJwtDecoder jwtDecoder() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -906,4 +955,4 @@ fun jwtDecoder(): ReactiveJwtDecoder {
return jwtDecoder
}
----
====
======

View File

@@ -17,8 +17,10 @@ In each case, two things need to be done and trade-offs are associated with how
One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, you can do so with the `JwtIssuerReactiveAuthenticationManagerResolver`:
====
.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
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:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
private Mono<ReactiveAuthenticationManager> addManager(
@@ -85,7 +90,8 @@ http
);
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
private fun addManager(
@@ -108,7 +114,7 @@ return http {
}
}
----
====
======
In this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` with a strategy for obtaining the `ReactiveAuthenticationManager` given to the issuer.
This approach lets us add and remove elements from the repository (shown as a `Map` in the preceding snippet) at runtime.

View File

@@ -23,7 +23,6 @@ When using https://spring.io/projects/spring-boot[Spring Boot], configuring an a
You can specify where the introspection endpoint is:
====
[source,yaml]
----
spring:
@@ -35,7 +34,6 @@ spring:
client-id: client
client-secret: secret
----
====
Where `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.
@@ -56,13 +54,11 @@ This startup process is quite a bit simpler than for JWTs, since no endpoints ne
Once the application has started, Resource Server tries to process any request containing an `Authorization: Bearer` header:
====
[source,http]
----
GET / HTTP/1.1
Authorization: Bearer some-token-value # Resource Server will process this
----
====
So long as this scheme is indicated, Resource Server tries to process the request according to the Bearer Token specification.
@@ -87,8 +83,10 @@ Once a token is authenticated, an instance of `BearerTokenAuthentication` is set
This means that it is available in `@Controller` methods when you use `@EnableWebFlux` in your configuration:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/foo")
@@ -97,7 +95,8 @@ public Mono<String> foo(BearerTokenAuthentication authentication) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/foo")
@@ -105,12 +104,14 @@ fun foo(authentication: BearerTokenAuthentication): Mono<String> {
return Mono.just(authentication.tokenAttributes["sub"].toString() + " is the subject")
}
----
====
======
Since `BearerTokenAuthentication` holds an `OAuth2AuthenticatedPrincipal`, that also means that it's available to controller methods, too:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@GetMapping("/foo")
@@ -119,7 +120,8 @@ public Mono<String> foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal pr
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@GetMapping("/foo")
@@ -127,7 +129,7 @@ fun foo(@AuthenticationPrincipal principal: OAuth2AuthenticatedPrincipal): Mono<
return Mono.just(principal.getAttribute<Any>("sub").toString() + " is the subject")
}
----
====
======
=== Looking Up Attributes with SpEL
@@ -135,8 +137,10 @@ You can access attributes with the Spring Expression Language (SpEL).
For example, if you use `@EnableReactiveMethodSecurity` so that you can use `@PreAuthorize` annotations, you can do:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@PreAuthorize("principal?.attributes['sub'] = 'foo'")
@@ -145,7 +149,8 @@ public Mono<String> forFoosEyesOnly() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@PreAuthorize("principal.attributes['sub'] = 'foo'")
@@ -153,7 +158,7 @@ fun forFoosEyesOnly(): Mono<String> {
return Mono.just("foo")
}
----
====
======
[[webflux-oauth2resourceserver-opaque-sansboot]]
== Overriding or Replacing Boot Auto Configuration
@@ -163,8 +168,10 @@ Spring Boot generates two `@Bean` instances for Resource Server.
The first is a `SecurityWebFilterChain` that configures the application as a resource server.
When you use an Opaque Token, this `SecurityWebFilterChain` looks like:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -178,7 +185,8 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -193,15 +201,17 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
If the application does not expose a `SecurityWebFilterChain` bean, Spring Boot exposes the default bean (shown in the preceding listing).
You can replace it by exposing the bean within the application:
.Replacing SecurityWebFilterChain
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -224,7 +234,8 @@ public class MyCustomSecurityConfiguration {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -242,7 +253,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
The preceding example requires the scope of `message:read` for any URL that starts with `/messages/`.
@@ -250,8 +261,10 @@ Methods on the `oauth2ResourceServer` DSL also override or replace auto configur
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
@@ -260,7 +273,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -268,7 +282,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return NimbusReactiveOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret)
}
----
====
======
If the application does not expose a `ReactiveOpaqueTokenIntrospector` bean, Spring Boot exposes the default one (shown in the preceding listing).
@@ -279,8 +293,10 @@ You can override its configuration by using `introspectionUri()` and `introspect
You can configure an authorization server's Introspection URI <<webflux-oauth2resourceserver-opaque-introspectionuri,as a configuration property>>, or you can supply in the DSL:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -303,7 +319,8 @@ public class DirectlyConfiguredIntrospectionUri {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -321,7 +338,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
Using `introspectionUri()` takes precedence over any configuration property.
@@ -330,8 +347,10 @@ Using `introspectionUri()` takes precedence over any configuration property.
`introspector()` is more powerful than `introspectionUri()`. It completely replaces any Boot auto-configuration of `ReactiveOpaqueTokenIntrospector`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -353,7 +372,8 @@ public class DirectlyConfiguredIntrospector {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -370,7 +390,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
This is handy when deeper configuration, such as <<webflux-oauth2resourceserver-opaque-authorization-extraction,authority mapping>>or <<webflux-oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, is necessary.
@@ -379,8 +399,10 @@ This is handy when deeper configuration, such as <<webflux-oauth2resourceserver-
Or, exposing a `ReactiveOpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -389,7 +411,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -397,26 +420,26 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return NimbusReactiveOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret)
}
----
====
======
[[webflux-oauth2resourceserver-opaque-authorization]]
== Configuring Authorization
An OAuth 2.0 Introspection endpoint typically returns a `scope` attribute, indicating the scopes (or authorities) it has been granted -- for example:
====
[source,json]
----
{ ..., "scope" : "messages contacts"}
----
====
When this is the case, Resource Server tries to coerce these scopes into a list of granted authorities, prefixing each scope with a string: `SCOPE_`.
This means that, to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@@ -436,7 +459,8 @@ public class MappedAuthorities {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -453,25 +477,28 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
}
}
----
====
======
You can do something similar with method security:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@PreAuthorize("hasAuthority('SCOPE_messages')")
public Flux<Message> getMessages(...) {}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasAuthority('SCOPE_messages')")
fun getMessages(): Flux<Message> { }
----
====
======
[[webflux-oauth2resourceserver-opaque-authorization-extraction]]
=== Extracting Authorities Manually
@@ -492,8 +519,10 @@ If the introspection response were as the preceding example shows, Resource Serv
You can customize behavior by using a custom `ReactiveOpaqueTokenIntrospector` that looks at the attribute set and converts in its own way:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
public class CustomAuthoritiesOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {
@@ -515,7 +544,8 @@ public class CustomAuthoritiesOpaqueTokenIntrospector implements ReactiveOpaqueT
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class CustomAuthoritiesOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -535,12 +565,14 @@ class CustomAuthoritiesOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector
}
}
----
====
======
Thereafter, you can configure this custom introspector by exposing it as a `@Bean`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -549,7 +581,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -557,7 +590,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return CustomAuthoritiesOpaqueTokenIntrospector()
}
----
====
======
[[webflux-oauth2resourceserver-opaque-jwt-introspector]]
== Using Introspection with JWTs
@@ -569,7 +602,6 @@ So, suppose you need to check with the authorization server on each request, in
Even though you are using the JWT format for the token, your validation method is introspection, meaning you would want to do:
====
[source,yaml]
----
spring:
@@ -581,7 +613,6 @@ spring:
client-id: client
client-secret: secret
----
====
In this case, the resulting `Authentication` would be `BearerTokenAuthentication`.
Any attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be whatever was returned by the introspection endpoint.
@@ -591,8 +622,10 @@ Now what?
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 {
@@ -618,7 +651,8 @@ public class JwtOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospect
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class JwtOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -641,12 +675,14 @@ class JwtOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
}
}
----
====
======
Thereafter, you can configure this custom introspector by exposing it as a `@Bean`:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -655,7 +691,8 @@ public ReactiveOpaqueTokenIntrospector introspector() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -663,7 +700,7 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return JwtOpaqueTokenIntrospector()
}
----
====
======
[[webflux-oauth2resourceserver-opaque-userinfo]]
== Calling a `/userinfo` Endpoint
@@ -679,8 +716,10 @@ The implementation in the next listing 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 {
@@ -709,7 +748,8 @@ public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntro
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -732,13 +772,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 {
@@ -754,7 +796,8 @@ public class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntro
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
@@ -767,12 +810,14 @@ class UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {
}
}
----
====
======
Either way, having created your `ReactiveOpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
@@ -781,7 +826,8 @@ ReactiveOpaqueTokenIntrospector introspector() {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
@@ -789,4 +835,4 @@ fun introspector(): ReactiveOpaqueTokenIntrospector {
return UserInfoOpaqueTokenIntrospector()
}
----
====
======

View File

@@ -4,8 +4,10 @@
For example, we can test our example from xref:reactive/authorization/method.adoc#jc-erms[EnableReactiveMethodSecurity] by using the same setup and annotations that we used in xref:servlet/test/method.adoc#test-method[Testing Method Security].
The following minimal sample shows 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 {
}
}
----
====
======

View File

@@ -2,8 +2,10 @@
After xref:reactive/test/web/setup.adoc[applying the Spring Security support to `WebTestClient`], we can use either annotations or `mutateWith` support -- for example:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;
@@ -64,7 +66,8 @@ public void messageWhenMutateWithMockAdminThenOk() throws Exception {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
import org.springframework.test.web.reactive.server.expectBody
@@ -111,6 +114,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].

View File

@@ -2,8 +2,10 @@
Spring Security also provides support for CSRF testing with `WebTestClient` -- for example:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
@@ -16,7 +18,8 @@ this.rest
...
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf
@@ -28,4 +31,4 @@ this.rest
.uri("/login")
...
----
====
======

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,10 @@
The basic setup looks like this:
====
.Java
[tabs]
======
Java::
+
[source,java,role="primary"]
----
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
@@ -31,7 +33,8 @@ public class HelloWebfluxMethodApplicationTests {
}
----
.Kotlin
Kotlin::
+
[source,kotlin,role="secondary"]
----
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity
@@ -58,4 +61,4 @@ class HelloWebfluxMethodApplicationTests {
// ...
}
----
====
======