当前位置: 首页 > 知识库问答 >
问题:

我怎么能完全拦截一个gRPC java一元调用,在客户端和服务器上?

洪国兴
2023-03-14

我正在将分布式系统代码库从SOAP(JAX-WS)迁移到gRPC java。我们使用这个代码库来教授远程调用、容错和安全实现。

在JAX-WS体系结构上,有一个可以拦截SOAP消息的拦截器类(称为SOAP处理程序)。您可以在客户端和服务器上配置处理程序。

作为参考,这是JAX-WS上远程调用的完整序列:

  • 客户端-创建端口(存根)并调用远程方法

通过这种方法,我们可以创建处理程序来记录SOAP消息,并添加安全性,如数字签名或加密。

我试图在Java(v1.17.2)上与gRPC具有类似的功能。

我基于我的gRPC代码在这个谷歌教程,一个简单的你好世界与一元方法。

基于这些示例,我编写了一个ClientInterceptor:

package example.grpc.client;
import java.util.Set;
import io.grpc.*;

public class HelloClientInterceptor implements ClientInterceptor {

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
        CallOptions callOptions, Channel channel) {
    return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
            channel.newCall(methodDescriptor, callOptions)) {

        @Override
        public void sendMessage(ReqT message) {
            System.out.printf("Sending method '%s' message '%s'%n", methodDescriptor.getFullMethodName(),
                    message.toString());
            super.sendMessage(message);
        }

        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
            System.out.println(HelloClientInterceptor.class.getSimpleName());

            ClientCall.Listener<RespT> listener = new ForwardingClientCallListener<RespT>() {
                @Override
                protected Listener<RespT> delegate() {
                    return responseListener;
                }

                @Override
                public void onMessage(RespT message) {
                    System.out.printf("Received message '%s'%n", message.toString());
                    super.onMessage(message);
                }
            };

            super.start(listener, headers);
        }
    };
}

}

我创建了一个服务器拦截器:

package example.grpc.server;

import java.util.Set;

import io.grpc.*;

public class HelloServerInterceptor implements ServerInterceptor {

@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
        ServerCallHandler<ReqT, RespT> serverCallHandler) {
    // print class name
    System.out.println(HelloServerInterceptor.class.getSimpleName());

    return Contexts.interceptCall(ctx, serverCall, metadata, serverCallHandler);
}

}

以下是(最后)我的问题:

  1. 服务器拦截器如何在方法执行前后看到消息

最终的目标是能够编写一个CipherClientHandler和一个CipherServerHandler来加密网络上的消息字节。我知道TLS在实践中是正确的方法,但我希望学生们做一个定制的实现。

感谢任何正确方向的指针!

共有1个答案

颜乐
2023-03-14

>

  • 通过"方法执行",我假设您的意思是"服务器执行方法,响应"。调用服务器方法的确切时间不是拦截API的一部分,也不应该依赖于此。对于今天的异步服务器处理程序,当调用serverListener.halfCloch()时,会调用服务器的方法。但同样,这不应该依赖。不清楚为什么这是必要的。

    服务器拦截器接收请求的ReqT消息和响应的RespT消息。要修改消息,只需在调用超级之前修改这些消息。

    客户端拦截器可以做与服务器拦截器相同的事情;在传递消息之前修改消息。

    请注意,当我说“修改消息”时,它通常会被实现为“制作一份经过适当修改的消息副本”

    但是,如果您想要加密/解密消息,那么从API流出来就不那么容易了,因为您正在完全更改它们的类型。您将获得一个请求,并将其转换为字节。为此,必须修改MethodDescriptors。

    在客户端,这可以在start()中完成,并向MethodDescriptor提供您自己的Marshallers。生成器。您可以访问应用程序的原始MethodDescriptor,因此可以使用它来序列化为字节。

    Marshaller ENCRYPTING_MARSHALLER = new Marshaller<InputStream>() {
      @Override
      public InputStream parse(InputStream stream) {
        return decrypt(stream);
      }
    
      @Override
      public InputStream stream(InputStream stream) {
        return encrypt(stream);
      }
    };
    
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> methodDescriptor,
            CallOptions callOptions, Channel channel) {
      ClientCall<InputStream, InputStream> call = channel.newCall(
          methodDescriptor.toBuilder(
            ENCRYPTING_MARSHALLER, ENCRYPTING_MARSHALLER),
          callOptions);
      // Can't use Forwarding* because the generics would break.
      // Note that all of this is basically boilerplate; the marshaller is
      // doing the work.
      return new ClientCall<ReqT, RespT>() {
        @Override
        public void halfClose() {
          call.halfClose();
        }
        // ... ditto for _all_ the other methods on ClientCall
    
        @Override
        public void sendMessage(ReqT message) {
          call.sendMessage(methodDescriptor.streamRequest(message));
        }
    
        @Override
        public void start(Listener<RespT> listener, Metadata headers) {
          call.start(new Listener<InputStream>() {
            @Override
            public void onHalfClose() {
              listener.onHalfClose();
            }
            // ... ditto for _all_ the other methods on Listener
    
            @Override
            public void onMessage(InputStream message) {
              listener.onMessage(methodDescriptor.parseResponse(message));
            }
          }, headers);
        }
      };
    }
    

    服务器端通常是相似的,但有点复杂,因为您需要重建ServerService定义,这不能作为一个普通的拦截器来完成。但是碰巧有一个实用程序可以做样板:

    ssd = ServerInterceptors.useMarshalledMessages(ssd, ENCRYPTING_MARSHALLER);
    

  •  类似资料:
    • 问题内容: 我想在Java-SE应用程序中使用拦截器,并且将weld作为CDI实现,并且在这里进行测试: 主班: 服务等级: 拦截器类: Aaa和输出: 我的问题 第一:为什么我在调用methodCallNumberTwo()时没有在methodCall()中调用拦截器? 第二:有办法改变吗? 我仅研究拦截器的行为,并且想了解。先感谢您! 问题答案: 不会调用拦截器,因为您是在对象的同一实例上调用

    • 我正在开发一个具有多个客户端的标准java RMI服务器。这些客户机有一个菜单,在那里他们可以调用服务器为他们做各种事情。 一种方法涉及一个队列,他们可以在其中将作业发送到队列并等待它得到处理。RMI服务器自动为所有客户端处理线程,但当涉及到此方法和队列时,我如何阻止此请求,例如: 首先调用客户端1,然后再调用客户端2(此处客户端1应首先从服务器接收消息,客户端2应等待服务器处理客户端1请求所需的

    • 我想同步2个集合。如果服务器端有什么变化,连接的客户端就会更新。我有一个非常基本的问题。我现在需要复制我的java项目,并在一个项目中编程服务器,在另一个项目中编程客户端吗?但这听起来像是相当多不必要的工作。我不能在一个项目中实现这一切,然后在一个主项目中启动服务器和客户端吗?我需要线程吗?我有点纠结于最好的方法是什么。提前谢谢。

    • 我想拦截mqtt客户端发送到artemis代理的消息。我遵循的是“拦截器-客户端-MQTT”示例。我的问题是我总是得到一个错误“java.lang.ClassNotFoundException:SimpleMQTTInterceptor”。我的问题是我应该把拦截器类放在哪里,以便代理可以找到它?我应该只放拦截器的类,还是放一个jar文件?

    • 问题内容: 我正在使用RMI编写密码系统的原型。 我有一个问题,因为当我启动两个客户端时,它们从OneTimePad类的服务器中的一个对象获得了响应。 因此客户端A获取为客户端b保留的密钥,由于特定的算法,这种情况不会发生。 服务器仅向客户端发送E和N变量(例如在RSA中),因此我无法序列化OneTimePad对象并通过网络发送它(因为它将具有所有密钥)。 如何为每个客户端创建OneTimePad

    • 我尝试使用Java中的Sockets连接到多个客户端。一切似乎都正常,但问题是,服务器只监听第一个客户端。如果有多个客户端,服务器可以向它们发送所有消息,但他可以只监听来自第一个客户端的消息。我尝试了所有这些(我从昨天开始就遇到了这个问题)。所以我很确定,错误一定在“ClientListener”类中。 说明:有一个客户端列表(用于与字符串通信的连接)。在GUI中有一个列表,我可以在其中选择要与哪