在 Kotlin Flow 中使用 ReactiveSecurityContextHolder

7
我正在使用Kotlin和CouchDB作为反应式数据库开发Spring Boot(2.2)项目,因此需要使用异步DAO(包括挂起函数或返回Flow的函数)。我想要设置WebFlux以实现异步控制器(再次强调,我希望返回的是Flow而不是Flux)。但我无法从ReactiveSecurityContextHolder中检索到我的安全上下文,根据我的阅读,与使用ThreadLocal来存储的SecurityContextHolder不同,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个回答

7

您真正想要实现的是从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()
}

当您需要从挂起方法访问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>"))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接