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