cd ~/goproject/grpcpro
项目中,server、client、protoc、cert目录都是并列的,go mod的模块是grpcpro
改造server.go
把
s := grpc.NewServer()
改成
creds, err := credentials.NewServerTLSFromFile("…/cert/server.crt", “…/cert/server.key”)
s := grpc.NewServer(grpc.Creds(creds))
package main
import (
"context"
"log"
"net"
pb "grpcpro/protoc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc"
)
const (
addr = "127.0.0.1:50052"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
creds, err := credentials.NewServerTLSFromFile("../cert/server.crt", "../cert/server.key")
s := grpc.NewServer(grpc.Creds(creds))
//s := grpc.NewServer()
pb.RegisterHelloServer(s, &server{})
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
改造client.go
之前的服务,没有证书,所以使用grpc.WithInsecure()跳过了证书验证
现在需要证书验证
方式1:
在客户端基于服务器的证书和服务器名字就可以对服务器进行验证
记得创建证书时:# openssl req -new -key server.key -subj “/CN=grpcpro1” -out server.csr
服务器名称是 grpcpro1
把conn, err := grpc.Dial(address, grpc.WithInsecure())
改成
creds, err := credentials.NewClientTLSFromFile("…/…/cert/server.crt", “grpcpro1”)
if err != nil {
log.Fatalf(“NewClientTLSFromFile: %v”, err)
}
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
package main
import (
"context"
"log"
"os"
"time"
"google.golang.org/grpc/credentials"
pb "grpcpro/protoc"
"google.golang.org/grpc"
)
const (
address = "127.0.0.1:50052"
defaultName = "world"
)
func main() {
// grpc.Dial负责和GRPC服务建立链接
creds, err := credentials.NewClientTLSFromFile("../cert/server.crt", "grpcpro1")
if err != nil {
log.Fatalf("NewClientTLSFromFile: %v", err)
}
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 然后NewGreeterClient函数基于已经建立的链接构造GreeterClient对象
// 返回的client其实是一个GreeterClient接口对象,通过接口定义的方法就可以调用服务端对应的GRPC服务提供的方法。
c := pb.NewHelloClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
//5秒超时
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Hello: %s", r.Message)
}
上面是简单的实现。
下面是比较完整的实现
server.go
使用http的http.ListenAndServeTLS代替net.Listen
package main
import (
"context"
"log"
"net/http"
pb "grpcpro/protoc"
"google.golang.org/grpc"
)
const (
addr = "127.0.0.1:50052"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
s := grpc.NewServer()
// var size = 1024 * 1024 * 1024 //1024M
// s := grpc.NewServer(grpc.MaxMsgSize(size), grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size))
pb.RegisterHelloServer(s, &server{})
if err:=http.ListenAndServeTLS(addr, "../cert/server.crt", "../cert/server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s.ServeHTTP(w, r) // GRPC Server
return
}),
);err!=nil{
panic(err)
}
}
client改造
client应该使用生成的client证书,在客户端就基于CA证书对服务器进行证书验证
ServerName还是之前的/CN: “grpcpro1”
package main
import (
"context"
"log"
"os"
"time"
"crypto/x509"
"crypto/tls"
"io/ioutil"
"google.golang.org/grpc/credentials"
pb "grpcpro/protoc"
"google.golang.org/grpc"
)
const (
address = "127.0.0.1:50052"
defaultName = "world"
)
func main() {
certificate, err := tls.LoadX509KeyPair("../cert/client.crt", "../cert/client.key")
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile("../cert/ca.crt")
if err != nil {
log.Fatal(err)
}
if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatal("failed to append ca certs")
}
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{certificate},
ServerName: "grpcpro1", // NOTE: this is required!
RootCAs: certPool,
})
// grpc.Dial负责和GRPC服务建立链接
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 然后NewGreeterClient函数基于已经建立的链接构造GreeterClient对象
// 返回的client其实是一个GreeterClient接口对象,通过接口定义的方法就可以调用服务端对应的GRPC服务提供的方法。
c := pb.NewHelloClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
//5秒超时
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Hello: %s", r.Message)
}
至此我们就启动了安全的GRPC服务了。否则按照之前的无证书访问,简直就是裸奔。。。