|
对于最新的稳定版本,请使用 Spring Security 6.3.3! |
|
对于最新的稳定版本,请使用 Spring Security 6.3.3! |
Spring Security 的 RSocket 支持依赖于 .
安全性的主要入口点位于 其中,它调整了 RSocket API 以允许拦截 with 实现。SocketAcceptorInterceptorPayloadSocketAcceptorInterceptorPayloadExchangePayloadInterceptor
您可以找到一些演示以下代码的示例应用程序:
-
你好 RSocket hellorsocket
最小 RSocket 安全配置
您可以在下面找到最小的 RSocket Security 配置:
-
Java
-
Kotlin
@Configuration
@EnableRSocketSecurity
public class HelloRSocketSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
@Configuration
@EnableRSocketSecurity
open class HelloRSocketSecurityConfig {
@Bean
open fun userDetailsService(): MapReactiveUserDetailsService {
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}
}
此配置启用简单身份验证,并设置 rsocket-authorization 以要求经过身份验证的用户执行任何请求。
添加 SecuritySocketAcceptorInterceptor
要使 Spring Security 正常工作,我们需要将 .
这就是将我们创建的原因与 RSocket 基础设施连接起来。SecuritySocketAcceptorInterceptorServerRSocketFactoryPayloadSocketAcceptorInterceptor
Spring 当你包含正确的依赖项时,Boot 会自动将其注册进来。RSocketSecurityAutoConfiguration
或者,如果您不使用 Boot 的自动配置,您可以通过以下方式手动注册它:
-
Java
-
Kotlin
@Bean
RSocketServerCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {
return (server) -> server.interceptors((registry) -> registry.forSocketAcceptor(interceptor));
}
@Bean
fun springSecurityRSocketSecurity(interceptor: SecuritySocketAcceptorInterceptor): RSocketServerCustomizer {
return RSocketServerCustomizer { server ->
server.interceptors { registry ->
registry.forSocketAcceptor(interceptor)
}
}
}
RSocket 身份验证
执行 RSocket 身份验证,它充当调用实例的控制器。AuthenticationPayloadInterceptorReactiveAuthenticationManager
设置与请求时的身份验证
通常,身份验证可以在设置时和/或请求时进行。
在少数情况下,设置时的身份验证是有意义的。 一种常见情况是当单个用户(即移动连接)利用 RSocket 连接时。 在这种情况下,只有一个用户正在利用连接,因此可以在连接时执行一次身份验证。
在共享 RSocket 连接的情况下,在每个请求上发送凭据是有意义的。 例如,作为下游服务连接到 RSocket 服务器的 Web 应用程序将建立所有用户都可以利用的单个连接。 在这种情况下,如果 RSocket 服务器需要根据 Web 应用程序的用户凭据执行授权,则每个请求是有意义的。
在某些情况下,在设置时和按请求进行身份验证是有意义的。
如前所述,请考虑 Web 应用程序。
如果我们需要将连接限制为 Web 应用程序本身,我们可以在连接时提供具有权限的凭证。
然后,每个用户将具有不同的权限,但不会具有不同的权限。
这意味着单个用户可以发出请求,但不能进行其他连接。SETUPSETUP
简单身份验证
Spring Security 支持简单身份验证元数据扩展。
|
Basic Authentication 草稿演变为 Simple Authentication,并且仅支持向后兼容。
请参阅设置。 |
RSocket 接收器可以解码凭据,该凭据是使用 DSL 的一部分自动设置的。
可以在下面找到显式配置。AuthenticationPayloadExchangeConvertersimpleAuthentication
-
Java
-
Kotlin
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.simpleAuthentication(Customizer.withDefaults());
return rsocket.build();
}
@Bean
open fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
rsocket
.authorizePayload { authorize -> authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
}
.simpleAuthentication(withDefaults())
return rsocket.build()
}
RSocket 发送者可以发送凭据,使用该凭据可以添加到 Spring 的 .SimpleAuthenticationEncoderRSocketStrategies
-
Java
-
Kotlin
RSocketStrategies.Builder strategies = ...;
strategies.encoder(new SimpleAuthenticationEncoder());
var strategies: RSocketStrategies.Builder = ...
strategies.encoder(SimpleAuthenticationEncoder())
然后,它可用于在设置中向接收器发送用户名和密码:
-
Java
-
Kotlin
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
Mono<RSocketRequester> requester = RSocketRequester.builder()
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(strategies.build())
.connectTcp(host, port);
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
val credentials = UsernamePasswordMetadata("user", "password")
val requester: Mono<RSocketRequester> = RSocketRequester.builder()
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(strategies.build())
.connectTcp(host, port)
或者,可以在请求中发送用户名和密码。
-
Java
-
Kotlin
Mono<RSocketRequester> requester;
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
public Mono<AirportLocation> findRadar(String code) {
return this.requester.flatMap(req ->
req.route("find.radar.{code}", code)
.metadata(credentials, authenticationMimeType)
.retrieveMono(AirportLocation.class)
);
}
import org.springframework.messaging.rsocket.retrieveMono
// ...
var requester: Mono<RSocketRequester>? = null
var credentials = UsernamePasswordMetadata("user", "password")
open fun findRadar(code: String): Mono<AirportLocation> {
return requester!!.flatMap { req ->
req.route("find.radar.{code}", code)
.metadata(credentials, authenticationMimeType)
.retrieveMono<AirportLocation>()
}
}
JWT
Spring Security 支持 Bearer Token Authentication Metadata Extension。 支持的形式包括对 JWT 进行身份验证(确定 JWT 有效),然后使用 JWT 做出授权决策。
RSocket 接收器可以解码凭据,该凭据是使用 DSL 的一部分自动设置的。
可以在下面找到示例配置:BearerPayloadExchangeConverterjwt
-
Java
-
Kotlin
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.jwt(Customizer.withDefaults());
return rsocket.build();
}
@Bean
fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
rsocket
.authorizePayload { authorize -> authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
}
.jwt(withDefaults())
return rsocket.build()
}
上面的配置依赖于存在的存在。
下面提供了从颁发者创建一个 ID 的示例:ReactiveJwtDecoder@Bean
-
Java
-
Kotlin
@Bean
ReactiveJwtDecoder jwtDecoder() {
return ReactiveJwtDecoders
.fromIssuerLocation("https://example.com/auth/realms/demo");
}
@Bean
fun jwtDecoder(): ReactiveJwtDecoder {
return ReactiveJwtDecoders
.fromIssuerLocation("https://example.com/auth/realms/demo")
}
RSocket 发送者不需要做任何特殊的事情来发送令牌,因为该值只是一个简单的 String。 例如,可以在设置时发送令牌:
-
Java
-
Kotlin
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
BearerTokenMetadata token = ...;
Mono<RSocketRequester> requester = RSocketRequester.builder()
.setupMetadata(token, authenticationMimeType)
.connectTcp(host, port);
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
val token: BearerTokenMetadata = ...
val requester = RSocketRequester.builder()
.setupMetadata(token, authenticationMimeType)
.connectTcp(host, port)
或者,令牌可以在请求中发送。
-
Java
-
Kotlin
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
Mono<RSocketRequester> requester;
BearerTokenMetadata token = ...;
public Mono<AirportLocation> findRadar(String code) {
return this.requester.flatMap(req ->
req.route("find.radar.{code}", code)
.metadata(token, authenticationMimeType)
.retrieveMono(AirportLocation.class)
);
}
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
var requester: Mono<RSocketRequester>? = null
val token: BearerTokenMetadata = ...
open fun findRadar(code: String): Mono<AirportLocation> {
return this.requester!!.flatMap { req ->
req.route("find.radar.{code}", code)
.metadata(token, authenticationMimeType)
.retrieveMono<AirportLocation>()
}
}
|
Basic Authentication 草稿演变为 Simple Authentication,并且仅支持向后兼容。
请参阅设置。 |
RSocket 授权
执行 RSocket 授权,它充当调用实例的控制器。
DSL 可用于根据 .
可以在下面找到示例配置:AuthorizationPayloadInterceptorReactiveAuthorizationManagerPayloadExchange
-
Java
-
Kotlin
rsocket
.authorizePayload(authz ->
authz
.setup().hasRole("SETUP") (1)
.route("fetch.profile.me").authenticated() (2)
.matcher(payloadExchange -> isMatch(payloadExchange)) (3)
.hasRole("CUSTOM")
.route("fetch.profile.{username}") (4)
.access((authentication, context) -> checkFriends(authentication, context))
.anyRequest().authenticated() (5)
.anyExchange().permitAll() (6)
);
rsocket
.authorizePayload { authz ->
authz
.setup().hasRole("SETUP") (1)
.route("fetch.profile.me").authenticated() (2)
.matcher { payloadExchange -> isMatch(payloadExchange) } (3)
.hasRole("CUSTOM")
.route("fetch.profile.{username}") (4)
.access { authentication, context -> checkFriends(authentication, context) }
.anyRequest().authenticated() (5)
.anyExchange().permitAll()
} (6)
| 1 | 设置连接需要权限ROLE_SETUP |
| 2 | 如果路由为 authorization,则仅要求对用户进行身份验证fetch.profile.me |
| 3 | 在此规则中,我们设置了一个自定义匹配器,其中 authorization 要求用户具有权限ROLE_CUSTOM |
| 4 | 此规则利用自定义授权。
匹配器表示一个变量,其名称在 .
该方法中公开了自定义授权规则。usernamecontextcheckFriends |
| 5 | 此规则可确保尚无规则的请求将要求对用户进行身份验证。 请求是包含元数据的位置。 它不会包含额外的负载。 |
| 6 | 此规则确保任何人都允许任何尚未制定规则的交易所。 在此示例中,这意味着没有元数据的负载没有授权规则。 |
请务必了解授权规则是按顺序执行的。 将仅调用匹配的第一个授权规则。
| 1 | 设置连接需要权限ROLE_SETUP |
| 2 | 如果路由为 authorization,则仅要求对用户进行身份验证fetch.profile.me |
| 3 | 在此规则中,我们设置了一个自定义匹配器,其中 authorization 要求用户具有权限ROLE_CUSTOM |
| 4 | 此规则利用自定义授权。
匹配器表示一个变量,其名称在 .
该方法中公开了自定义授权规则。usernamecontextcheckFriends |
| 5 | 此规则可确保尚无规则的请求将要求对用户进行身份验证。 请求是包含元数据的位置。 它不会包含额外的负载。 |
| 6 | 此规则确保任何人都允许任何尚未制定规则的交易所。 在此示例中,这意味着没有元数据的负载没有授权规则。 |