gRPC基于JDK的TLS通信

gRPC基于JDK的TLS通信

首先根据这里分享的链接 OpenSSL Certificate Authority(http://my.oschina.net/xinxingegeya/blog?catalog=3452471)实现一个私有的CA,并使用这个CA来签发证书。

定义gRPC 客户端和服务器端通信的protobuf文件,

syntax = "proto3";
 
package helloworld;
 
option java_multiple_files = true;
option java_package = "com.usoft.grpc.example.helloworld";
option java_outer_classname = "HelloWorldProto";
 
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloResponse {
  string message = 1;
}

下面来看服务器端如何编写,主要代码如下,

private void start() throws Exception {
    // 加载服务器端的证书
    File cert = Utils.loadCert("www.helloworld.com.cert.pem");
    // 加载服务器端的密钥文件
    File key = Utils.loadCert("www.helloworld.com.key.pkcs8.pem");
 
    // 通过服务器端的证书和密钥文件构件SslContextBuilder
    SslContextBuilder sslContextBuilder = GrpcSslContexts.forServer(cert,
        key);
 
    sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder,
        SslProvider.JDK);
 
    //使用netty 作为gRPC的服务器
    final EventLoopGroup boss = new NioEventLoopGroup();
    final EventLoopGroup worker = new NioEventLoopGroup();
    final Class<? extends ServerChannel> channelType = NioServerSocketChannel.class;
 
    //addService(GreeterGrpc.bindService(new HelloWorldServiceImpl())) 添加服务接口的实现类,绑定该服务
    //启动服务器
    server = NettyServerBuilder.forPort(port).bossEventLoopGroup(boss)
        .workerEventLoopGroup(worker).channelType(channelType)
        .addService(GreeterGrpc.bindService(new HelloWorldServiceImpl()))
        .sslContext(sslContextBuilder.build()).build().start();
 
    logger.info("Server started, listening on " + port);
    // 注册服务器钩子
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            // Use stderr here since the logger may have been reset by its JVM shutdown hook.
            System.err.println(
                "*** shutting down gRPC server since JVM is shutting down");
            GreeterServer.this.stop();
            System.err.println("*** server shut down");
        }
    });
}

下面来看客户端的实现,主要代码如下,

package com.usoft;
 
import com.usoft.grpc.example.helloworld.GreeterGrpc;
import com.usoft.grpc.example.helloworld.HelloRequest;
import com.usoft.grpc.example.helloworld.HelloResponse;
import io.grpc.ManagedChannel;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
 
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
 
/**
 * Created by xinxingegeya on 16/1/2.
 */
public class GreeterClientTest {
    private static final Logger logger = Logger
        .getLogger(GreeterClientTest.class.getName());
 
    private String address = "www.helloworld.com:8888";
 
    private ManagedChannel channel;
 
    private GreeterGrpc.GreeterBlockingStub greeterBlockingStub;
    private GreeterGrpc.GreeterStub greeterStub;
 
    @Before
    public void setup() throws IOException {
        // It's a Netty transport.
        NegotiationType negotiationType = NegotiationType.TLS;
        // 客户端加载 证书链,来验证服务器端的证书是否可信
        File cert = Utils.loadCert("ca-chain.cert.pem");
        SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient()
            .trustManager(cert);
        sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder,
            SslProvider.JDK);
        SslContext sslContext = sslContextBuilder.build();
 
        final EventLoopGroup group = new NioEventLoopGroup();
        final Class<? extends io.netty.channel.Channel> channelType = NioSocketChannel.class;
        // 构建和服务器端通信的 channel,基于netty。
        NettyChannelBuilder builder = NettyChannelBuilder
            .forAddress(Utils.parseSocketAddress(address)).eventLoopGroup(group)
            .channelType(channelType).negotiationType(negotiationType)
            .sslContext(sslContext);
 
        channel = builder.build();
        greeterBlockingStub = GreeterGrpc.newBlockingStub(channel);
        greeterStub = GreeterGrpc.newStub(channel);
    }
 
    @After
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }
 
 
    // 通过stub来和服务器端进行通信
    @Test
    public void testGreeter() {
        HelloRequest request = HelloRequest.newBuilder().setName("liyanxin")
            .build();
        HelloResponse resp1 = greeterBlockingStub.sayHello(request);
        HelloResponse resp2 = greeterBlockingStub.sayHello(request);
        HelloResponse resp3 = greeterBlockingStub.sayHello(request);
        HelloResponse resp4 = greeterBlockingStub.sayHello(request);
 
        System.out.println(resp1.getMessage());
        System.out.println(resp2.getMessage());
        System.out.println(resp3.getMessage());
        System.out.println(resp4.getMessage());
    }
 
}

需要注意的是 ,不论启动客户端还是服务器都需要这样来配置 JVM 参数,

java -Xbootclasspath/p:<path_to_alpn_boot_jar> ...

path_to_alpa_boot_jar是一个 jar 的路径,这个jar包jetty-alpa-boot.jar http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-versions

什么是 ALPN,

http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-introduction

https://zlb.me/2013/07/19/npn-and-alpn/

http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-01

详细代码请见http://git.oschina.net/xinxingegeya/gRPC-sample

==========END==========

你可能感兴趣的:(gRPC基于JDK的TLS通信)