我正在将分布式系统代码库从SOAP(JAX-WS)迁移到gRPC-java。 我们使用此代码库来教远程调用,容错和安全性实现。
在JAX-WS架构上,有一个拦截器类(称为SOAP处理程序)可以拦截SOAP消息。您可以在客户端和服务器上配置处理程序。
作为参考,这是在JAX-WS上进行远程调用的完整序列:
使用这种方法,我们可以创建处理程序以记录SOAP消息,并增加安全性,例如数字签名或加密。
我正在尝试在Java(v1.17.2)上的gRPC具有类似的功能。
我的gRPC代码基于this google tutorial,这是一个具有一元方法的简单问候世界。
基于these examples,我写了 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);
}
};
}
}
我已经创建了 ServerInterceptor :
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);
}
}
(最后)是我的问题:
最终目标是能够编写一个CipherClientHandler和一个CipherServerHandler,它们将对线路上的消息字节进行加密。我知道TLS是实践中正确的方法,但我希望学生进行自定义实现。
感谢任何指向正确方向的指针!
答案 0 :(得分:0)
通过“方法执行”,我假设您的意思是前面的“服务器-执行方法,响应”。调用服务器方法的确切时间不属于拦截API的一部分,因此不应依赖于该时间。在当今使用异步服务器处理程序的情况下,碰巧在调用collection_id=56
时调用了服务器的方法。但是同样,这不应该依赖。目前尚不清楚为什么这样做是必要的。
服务器拦截器接收用于请求的serverListener.halfClose()
和用于响应的ReqT message
。要修改消息,只需在调用RespT message
之前修改这些消息即可。
客户端拦截器可以与服务器拦截器相同;在传递消息之前先对其进行修改。
请注意,当我说“修改邮件”时,通常将其实现为“对邮件进行适当修改后进行复制。”
但是,如果您要加密/解密消息,那么从API中获取消息就不会那么容易了,因为您正在完全更改消息的类型。给您一个super
,并将其转换为字节。为此,您必须修改ReqT
。
在客户端,这可以在MethodDescriptor
内完成,并向start()
提供您自己的Marshaller
。您可以访问应用程序的原始MethodDescriptor.Builder
,因此可以使用它来序列化为字节。
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);
}
};
}
,而这不能作为普通的拦截器来完成。但是碰巧有一个实用工具可以完成样板工作:
ServerServiceDefinition