Merge branch '5.8.x' into 6.0.x
Closes gh-13406
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user