最近开始接触到grpc并用它写了一些接口,使用了Spring Security来做用户身份校验,但当我写的正起劲的时候发现了一个问题

生成的service里面的方法只有request和response,这就让我很头疼了,总不可能在request里面再放一个token吧

@GrpcService
class TestService:TestrdServiceGrpc.TestServiceImplBase() {
    override fun method(
        request: methodRequest,
        responseObserver: StreamObserver<methodResponse>
    ) {
        super.method(request, responseObserver)
    }
}

最开始想到的是通过SecurityContextHolder.getContext().authentication来获取,但是获取到的变量一直都是null,经过一番折腾,发现在运行到DefaultAuthenticatingServerInterceptor的一段代码的时候就清空了

......
finally {
    SecurityContextHolder.clearContext();
    ......
    log.debug("startCall - Authentication cleared");
}

而执行clearContext();的此刻还远远没到执行我所写的代码

就在我满脑子问号的时候,向上翻阅源代码的时候偶然间发现了这行代码,在这里把Authentication给👴塞到了Context里边

final Context context = Context.current().withValue(AUTHENTICATION_CONTEXT_KEY, authentication);

好家伙,原来是给存到了Context里面。经过我反复查看Context的源代码,找到了获取该变量的方法

/**
 * Get the value from the {@link #current()} context for this key.
 */
@SuppressWarnings("unchecked")
public T get() {
    return get(Context.current());
}
@GrpcService
class TestService : TestServiceGrpc.TestServiceImplBase() {
    override fun method(
        request: Request,
        responseObserver: StreamObserver<Response>
    ) {
        val authentication = AUTHENTICATION_CONTEXT_KEY.get()
        val response = Response {
            message = "hello ${authentication.principal}"
        }
        responseObserver.onNext(response)
        responseObserver.onCompleted()
    }
}

当然,你也可以自己向Context里面放变量,更多用法可参考Github上别人的例子

//存放
Context.current().withValue(KEY, VALUE)
//读取
KEY.get()