![](/img/trans.png)
[英]How can I intercept a method call to a library class in Java or Android?
[英]How can I fully intercept a gRPC java unary call, on the client, and on the server?
我正在将分布式系统代码库从SOAP(JAX-WS)迁移到gRPC-java。 我们使用此代码库来教远程调用,容错和安全性实现。
在JAX-WS体系结构上,有一个拦截器类(称为SOAP处理程序)可以拦截SOAP消息。 您可以在客户端和服务器上配置处理程序。
作为参考,这是在JAX-WS上进行远程调用的完整序列:
使用这种方法,我们可以创建处理程序来记录SOAP消息,并增加安全性,例如数字签名或加密。
我正在尝试在Java(v1.17.2)上具有类似的功能。
我在这个Google教程中建立了我的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);
}
};
}
}
我创建了一个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是实践中正确的方法,但我希望学生进行自定义实现。
感谢您指出正确的方向!
通过“方法执行”,我假设您的意思是前面的“服务器-执行方法,响应”。 调用服务器方法的确切时间不属于拦截API的一部分,因此不应依赖于该时间。 在当今使用异步服务器处理程序的情况下,碰巧在调用serverListener.halfClose()
时调用了服务器的方法。 但是同样,这不应该依赖。 目前尚不清楚为什么这是必要的。
服务器拦截器接收ReqT message
用于请求和RespT message
响应。 要修改消息,只需在调用super
之前修改这些消息即可。
客户端拦截器可以与服务器拦截器相同。 在传递消息之前先对其进行修改。
注意,当我说“修改消息”时,通常将其实现为“使用适当的修改来复制消息”。
但是,如果您要加密/解密消息,那么从API中获取消息就不会那么容易了,因为您正在完全更改消息的类型。 您将获得一个ReqT
,并将其转换为字节。 为此,您必须修改MethodDescriptor
。
在客户端,这可以在start()
,并向MethodDescriptor.Builder
提供您自己的Marshaller
。 您可以访问应用程序的原始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
,而这不能作为普通的拦截器来完成。 但是碰巧有一个实用工具可以执行样板工作:
ssd = ServerInterceptors.useMarshalledMessages(ssd, ENCRYPTING_MARSHALLER);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.