[英]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.