grpc-server 自定义拦截器实现,适用于server端做鉴权认证场景
在上篇文章 springboot集成grpc服务 基础上,拦截器实现如下
@Slf4j
@GrpcGlobalServerInterceptor
@Component
public class AuthServerInterceptor implements ServerInterceptor {
private final static String TOKEN = "test_token";
private final static String APPID = "test_appId";
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
//获取客户端参数
log.info("header received from client:" + metadata);
Metadata.Key<String> token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
final Metadata.Key<String> appId = Metadata.Key.of("appId", Metadata.ASCII_STRING_MARSHALLER);
String tokenStr = metadata.get(token);
final String appIdStr = metadata.get(appId);
if(!(APPID.equals(appIdStr)&& TOKEN.equals(tokenStr))) {
//认证失败,关闭连接
log.info("appId:{},token:{} 认证失败,关闭连接",appIdStr,tokenStr);
serverCall.close(Status.DATA_LOSS,metadata);
}
//服务端写回参数
ServerCall<ReqT, RespT> call = new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(serverCall) {
@Override
public void sendHeaders(Metadata headers) {
//由于grpc服务是全双工的,因此我们也可以向客户端推送 Metadata 数据
headers.put(appId,appIdStr);
super.sendHeaders(headers);
}
};
return serverCallHandler.startCall(call,metadata);
}
}
grpc-client 如何在header中设置参数?
1、只设置客户端请求时附带的header,见类 io.grpc.stub.MetadataUtils,其中有个工具方法可直接设置:
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1789")
public static <T extends AbstractStub<T>> T attachHeaders(T stub, Metadata extraHeaders) {
return stub.withInterceptors(new ClientInterceptor[]{newAttachHeadersInterceptor(extraHeaders)});
}
Metadata参数对于调用者不太友好,我们希望入参可能是个Map<String,String>,可以简单封装下
private static <T extends AbstractStub<T>> T attachHeaders(T stub, final Map<String, String> headerMap) {
Metadata extraHeaders = new Metadata();
if (headerMap != null) {
for (String key : headerMap.keySet()) {
Metadata.Key<String> customHeadKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER);
extraHeaders.put(customHeadKey, headerMap.get(key));
}
}
return MetadataUtils.attachHeaders(stub, extraHeaders);
}
好了,接下来我们可以使用上述工具来设置client端header参数了
private ManagedChannel channel;
private GreeterGrpc.GreeterBlockingStub greeterBlockingStub;
@Before
public void init () {
//通道初始化 建立连接
log.info(" iat connect start.");
channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9099)
.usePlaintext()
.build();//池化处理 成本高
greeterBlockingStub = GreeterGrpc.newBlockingStub(channel);
//设置header参数
Map<String,String> headerMap = new HashMap<>();
headerMap.put("appId","427141404052612");
headerMap.put("token","782bc51504f805ca7eea9209af137100");
//这里是关键,请注意一定要把greeterBlockingStub存根再次赋值,因为MetadataUtils.attachHeaders底层
//也是通过注入客户端拦截器重新生成的存根。
greeterBlockingStub = attachHeaders(greeterBlockingStub,headerMap);
}
2、支持设置客户端请求的header以及获取服务端返回结果中的header,我们需要定义客户端拦截器来实现
/**
* 客户端拦截器
*/
private static class MyClientInterceptor implements ClientInterceptor {
//客户端header的key
static final Metadata.Key<String> TOKEN = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
//客户端header的key
static final Metadata.Key<String> APPID = Metadata.Key.of("appId", Metadata.ASCII_STRING_MARSHALLER);
@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 start(Listener<RespT> responseListener, Metadata headers) {
//放入客户端的header
headers.put(TOKEN, "05e0450b40a30bb7e92f3239c2f9e389");
headers.put(APPID, "227141404052613");
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
@Override
public void onHeaders(Metadata headers) {
//输出服务端传递回来的header
System.out.println("header received from server:" + headers);
super.onHeaders(headers);
}
}, headers);
}
};
}
}
好了,那么我们在客户端建立连接时,注入我们的拦截器。
private ManagedChannel channel;
private IatGrpc.IatStub iatStub;
@Before
public void init () {
log.info(" iat connect start.");
channel = ManagedChannelBuilder.forAddress("127.0.0.1", 50051)
.usePlaintext()
.build();
//客户端通道绑定拦截器
Channel tmpChannel = ClientInterceptors.intercept(channel, new MyClientInterceptor());
iatStub = IatGrpc.newStub(tmpChannel);
}
参考文章:
gRPC请求中对header进行处理
网友评论