CodeWalk

gRPC拦截器实现认证与日志

作者:孤独的心 · 2026-05-30 12:55

请详细解释gRPC的**拦截器(Interceptor)**机制。如何在gRPC中实现客户端拦截器(ClientInterceptor)和服务端拦截器(ServerInterceptor)?如何用拦截器实现JWT认证、请求日志记录、超时控制和异常处理?gRPC拦截器与Spring MVC的Interceptor有什么异同?

回答

孤独的心

gRPC拦截器类型

类型作用注册方式
ServerInterceptor服务端拦截请求/响应ServerBuilder.intercept()
ClientInterceptor客户端拦截请求/响应ManagedChannelBuilder.intercept()

服务端拦截器示例

public class AuthInterceptor implements ServerInterceptor {
    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
            ServerCall<ReqT, RespT> call,
            Metadata headers,
            ServerCallHandler<ReqT, RespT> next) {

        // 提取JWT Token
        String authHeader = headers.get(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER));
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            call.close(Status.UNAUTHENTICATED.withDescription("缺少Token"), new Metadata());
            return new ServerCall.Listener<>() {};
        }

        String token = authHeader.substring(7);
        try {
            // 验证JWT
            Claims claims = Jwts.parser().verifyWith(publicKey).build().parseSignedClaims(token).getPayload();
            Context ctx = Context.current().withValue(USER_ID_KEY, claims.getSubject());
            return Contexts.interceptCall(ctx, call, headers, next);
        } catch (Exception e) {
            call.close(Status.UNAUTHENTICATED.withDescription("Token无效"), new Metadata());
            return new ServerCall.Listener<>() {};
        }
    }
}

客户端拦截器示例

public class LoggingInterceptor implements ClientInterceptor {
    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions,
            Channel next) {

        return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
            @Override
            public void sendMessage(ReqT message) {
                log.info("发送请求: {} {}", method.getFullMethodName(), message);
                super.sendMessage(message);
            }

            @Override
            public void start(Listener<RespT> responseListener, Metadata headers) {
                super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
                    @Override
                    public void onMessage(RespT message) {
                        log.info("收到响应: {}", message);
                        super.onMessage(message);
                    }
                }, headers);
            }
        };
    }
}

注册拦截器

// 服务端
Server server = ServerBuilder.forPort(50051)
    .addService(ServerInterceptors.intercept(userService, 
        new AuthInterceptor(), new LoggingInterceptor(), new RateLimitInterceptor()))
    .build();

// 客户端
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
    .intercept(new AuthClientInterceptor(token), new LoggingInterceptor())
    .usePlaintext()
    .build();

与Spring MVC Interceptor对比

维度gRPC InterceptorSpring MVC Interceptor
触发时机方法调用前后(gRPC Call)HTTP请求前后
上下文传递Context(ThreadLocal替代方案)Request Attributes
流式处理可拦截消息级别无流式概念
元数据Metadata(类似HTTP Headers)HttpServletRequest/Response