在 Kotlin 流中使用 ReactiveSecurityContextHolder

Posted

技术标签:

【中文标题】在 Kotlin 流中使用 ReactiveSecurityContextHolder【英文标题】:Using ReactiveSecurityContextHolder inside a Kotlin Flow 【发布时间】:2020-03-21 19:44:22 【问题描述】:

我正在使用 Kotlin 开发 Spring Boot (2.2) 项目,使用 CouchDB 作为(反应式)数据库,因此使用异步 DAO(挂起函数或返回 Flow 的函数)。我正在尝试设置 WebFlux 以便也拥有异步控制器(再次,我想返回 Flows,而不是 Flux)。但我无法从ReactiveSecurityContextHolder 检索我的安全上下文。

根据我的阅读,与 SecurityContextHolder 使用 ThreadLocal 存储它不同,ReactiveSecurityContextHolder 依赖于这样一个事实,即 Spring 在订阅我的反应链时,还将该上下文存储在该链中,从而允许我从链内调用ReactiveSecurityContextHolder.getContext()

问题是我必须在某个时候将我的Mono<SecurityContext> 转换为 Flow,这让我失去了我的SecurityContext。所以我的问题是:有没有办法让 Spring Boot 控制器在我的逻辑中从 ReactiveSecurityContextHolder 检索安全上下文时返回 Flow?基本上简化后应该是这样的:

@GetMapping
fun getArticles(): Flow<String> 
    return ReactiveSecurityContextHolder.getContext().flux().asFlow() // returns nothing

请注意,如果我直接返回 Flux(跳过.asFlow()),或者最后添加.single().toList()(因此使用suspend fun),那么它可以正常工作并且我的安全上下文是回来了,但这又不是我想要的。我想解决方案是将上下文从 Flux(来自ReactiveSecurityContextHolder 的初始反应链)转移到 Flow,但默认情况下似乎没有这样做。

编辑:这是一个展示问题的示例项目:https://github.com/Simon3/webflux-kotlin-sample

【问题讨论】:

【参考方案1】:

您真正尝试实现的是从 Flow 内部访问您的 ReactorContext。

一种方法是放宽返回 Flow 的需求,改为返回 Flux。这允许您恢复 ReactorContext 并将其传递给您将用于生成数据的 Flow。

@ExperimentalCoroutinesApi
@GetMapping("/flow")
fun flow(): Flux<Map<String, String>> = Mono.subscriberContext().flatMapMany  reactorCtx ->
    flow 
        val ctx = coroutineContext[ReactorContext.Key]?.context?.get<Mono<SecurityContext>>(SecurityContext::class.java)?.asFlow()?.single()
        emit(mapOf("user" to ((ctx?.authentication?.principal as? User)?.username ?: "<NONE>")))
    .flowOn(reactorCtx.asCoroutineContext()).asFlux()

如果你需要从一个suspend方法访问ReactorContext,你可以简单地从coroutineContext中取回它,无需任何技巧:

@ExperimentalCoroutinesApi
@GetMapping("/suspend")
suspend fun suspend(): Map<String,String> 
    val ctx = coroutineContext[ReactorContext.Key]?.context?.get<Mono<SecurityContext>>(SecurityContext::class.java)?.asFlow()?.single()
    return mapOf("user" to ((ctx?.authentication?.principal as? User)?.username ?: "<NONE>"))

【讨论】:

以上是关于在 Kotlin 流中使用 ReactiveSecurityContextHolder的主要内容,如果未能解决你的问题,请参考以下文章

可以用多个收集器收集的 Kotlin 流(或类似的东西)

Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )

Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )

自定义声明包含在隐式流中,但不包含在 PKCE 流中

你啥时候在流中使用接口而不是类型别名?

在 apache spark 流中使用 foreachRDD 内的数据库连接