如何访问服务中的标头?

问题描述

我正在尝试在后端处理 gRPC 中的 JWT 身份验证。我可以在拦截器中提取 JWT,但如何在我的服务中访问它?我认为应该使用 CoroutineContextServerInterceptor 来完成,但这不起作用:

val jwtKey: Context.Key<String> = Context.key("jwtKey")

fun main() {
    ServerBuilder.forPort(8980).intercept(UserInjector).addService(MyService).build().start().awaitTermination()
}

object UserInjector : CoroutineContextServerInterceptor() {
    override fun coroutineContext(call: ServerCall<*,*>,headers: Metadata): CoroutineContext {
        val jwtString = headers.get(Metadata.Key.of("jwt",Metadata.ASCII_STRING_MARSHALLER))
        println("coroutineContext: $jwtString")
        return GrpcContextElement(Context.current().withValue(jwtKey,jwtString))
    }
}

object MyService : MyServiceGrpcKt.MyServiceCoroutineImplBase() {
    override suspend fun testingJWT(request: Test.MyRequest): Test.MyResponse {
        println("testingJWT: ${jwtKey.get()}")
        return Test.MyResponse.getDefaultInstance()
    }
}

输出

coroutineContext: something
testingJWT: null

解决方法

我认为你需要在它自己的协程上下文元素中传播它。

class JwtElement(val jwtString: String) : CoroutineContext.Element {
  companion object Key : CoroutineContext.Key<JwtElement>
  override val key: CoroutineContext.Key<JwtElement>
    get() = Key
}

object UserInjector : CoroutineContextServerInterceptor() {
  override fun coroutineContext(call: ServerCall<*,*>,headers: Metadata): CoroutineContext {
    val jwtString = headers.get(Metadata.Key.of("jwt",Metadata.ASCII_STRING_MARSHALLER))
    println("coroutineContext: $jwtString")
    return JwtElement(jwtString)
  }
}

object MyService : MyServiceGrpcKt.MyServiceCoroutineImplBase() {
  override suspend fun testingJWT(request: Test.MyRequest): Test.MyResponse {
    println("testingJWT: ${coroutineContext[JwtElement]}")
    return Test.MyResponse.getDefaultInstance()
  }
}