gRPC如何调整拦截器顺序 实现请求体读取优先于授权头校验
问题根因
- 拦截器执行逻辑的时机不匹配:gRPC的
interceptCall方法会在框架接收到请求头、还未开始解析请求体时就触发,而你读取请求体写入header的逻辑写在onMessage回调里,该回调要等到interceptCall完全执行结束、框架开始解析请求体时才会触发,所以哪怕你调整拦截器注册顺序,授权拦截器在校验时也拿不到写入的Authorization头。 - 你对拦截器注册顺序的规则理解反了:gRPC Java中
ServerInterceptors.intercept的拦截器参数,请求的处理顺序是从最后一个参数到第一个参数依次执行,你当前的注册顺序其实FirstInterceptorReadTokenFromMessageBody的interceptCall会比SecondInterceptorHeaderAuthorization先触发,本身注册顺序是符合你的需求的,不需要调整。
调整方案
方案1:调整授权逻辑的执行时机(复用原有两套拦截器逻辑,改动最小)
保持你当前的拦截器注册顺序不变,把SecondInterceptorHeaderAuthorization里的授权校验逻辑从interceptCall方法移到它的ForwardingServerCallListener的onMessage回调里,保证校验逻辑在你写入header之后执行,示例代码如下:
public class SecondInterceptorHeaderAuthorization implements ServerInterceptor { @Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>( next.startCall(call, headers)) { @Override public void onMessage(ReqT request) { // 这里执行原来的授权校验逻辑,此时Authorization头已经被前序的onMessage写入 Metadata.Key<String> key = Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER); String token = headers.get(key); // 原有校验逻辑,校验失败直接返回权限错误 if (token == null || !authValidate(token)) { call.close(Status.UNAUTHENTICATED.withDescription("无效的授权凭证"), new Metadata()); return; } super.onMessage(request); } }; } }
如果是Unary请求(单请求单响应),这个方案完全可用;如果是客户端流请求,需要增加逻辑保证只在第一次收到请求时执行一次授权校验即可。
方案2:合并拦截器逻辑(性能更优,没有多余的链路嵌套)
直接把授权校验逻辑整合到第一个读取token的拦截器里,或者在第一个拦截器拿到token之后直接调用原有授权逻辑的工具类,不需要再通过header传递、走两层拦截器,代码更简洁,也没有执行时机的问题。
方案可行性说明
你原本的设计思路是完全可行的,只要调整好两个拦截器逻辑的执行时机即可正常运行,不需要重构核心的授权校验逻辑。
内容的提问来源于stack exchange,提问作者walshbm15
相关产品推荐
相关产品推荐

