我正在使用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
}
请注意,如果我直接返回通量(跳过.asFlow()
),或者最后添加.single()
或.toList()
(因此使用suspend fun
),则它工作正常,并返回了我的安全上下文,但这又不是我想要的。我猜解决方案是将上下文从Flux(从ReactiveSecurityContextHolder
开始的初始反应链)转移到Flow,但是默认情况下似乎没有这样做。
编辑:这是一个展示问题的示例项目:https://github.com/Simon3/webflux-kotlin-sample
答案 0 :(得分: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>"))
}