簡單說一下我的環境 win7+go1.15.6,GO1.15 X509 不能用了 ,
證書
需要用到SAN證書,下面就介紹一下SAN證書生成。首先需要下載 OpenSSL http://slproweb.com/products/Win32OpenSSL.html
第1步:生成 CA 根證書
openssl genrsa -out ca.key 2048 openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:cn State or Province Name (full name) [Some-State]:shanghai Locality Name (eg, city) []:shanghai Organization Name (eg, company) [Internet Widgits Pty Ltd]:custer Organizational Unit Name (eg, section) []:custer Common Name (e.g. server FQDN or YOUR name) []:localhost Email Address []:
第2步:用 openssl 生成 ca 和雙方 SAN 證書。
准備默認 OpenSSL 配置文件於當前目錄
linux系統在 : /etc/pki/tls/openssl.cnf
Mac系統在: /System/Library/OpenSSL/openssl.cnf
Windows:安裝目錄下 openssl.cfg 比如 D:\Program Files\OpenSSL-Win64\bin\openssl.cfg
1:拷貝配置文件到項目 然后修改
2:找到 [ CA_default ],打開 copy_extensions = copy
3:找到[ req ],打開 req_extensions = v3_req # The extensions to add to a certificate request
4:找到[ v3_req ],添加 subjectAltName = @alt_names
5:添加新的標簽 [ alt_names ] , 和標簽字段
[ alt_names ] DNS.1 = localhost DNS.2 = *.custer.fun
這里填入需要加入到 Subject Alternative Names 段落中的域名名稱,可以寫入多個。
第3步:生成服務端證書
openssl genpkey -algorithm RSA -out server.key openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=cn/OU=custer/O=custer/CN=localhost" -config ./openssl.cfg -extensions v3_req openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req
server.csr是上面生成的證書請求文件。ca.pem/ca.key是CA證書文件和key,用來對server.csr進行簽名認證。這兩個文件在之前生成的。
第4步:生成客戶端證書
openssl genpkey -algorithm RSA -out client.key openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=cn/OU=custer/O=custer/CN=localhost" -config ./openssl.cfg -extensions v3_req openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req
現在 Go 1.15 以上版本的 GRPC 通信,這樣就完成了使用自簽CA、Server、Client證書和雙向認證
GPRC
1.安裝protobuf , https://github.com/protocolbuffers/protobuf/releases可以下載【protoc-3.14.0-win64.zip】, 然后配置到環境變量Path中 ,protoc --version 查看版本如下:
2,。安裝相應的包
go get -u github.com/golang/protobuf/proto //安裝 golang 的proto工具包 go get -u github.com/golang/protobuf/protoc-gen-go //安裝 goalng 的proto編譯支持 go get -u google.golang.org/grpc //安裝 GRPC 包
3.創建 proto 文件#
proto 文件是微服務交互的基本,proto的語法可見GoogleDocs,這里簡單寫一個示例(spider.proto)
syntax = "proto3"; // 協議為proto3 package spider; // 包名 // 發送請求 message SendAddress { // 發送的參數字段 // 參數類型 參數名 標識號(不可重復) string address = 1; // 要請求的地址 string method = 2; // 請求方式 } // 返回響應 message GetResponse { // 接收的參數字段 // 參數類型 參數名 標識號 int32 httpCode = 1; // http狀態碼 string response = 2; // 返回體 } // 定義服務,可定義多個服務,每個服務可多個接口 service GoSpider { // rpc請求 請求的函數 (發送請求參數) returns (返回響應的參數) rpc GetAddressResponse (SendAddress) returns (GetResponse); }
4.生成 .bp.go
文件# 使用剛才下載的 protoc 工具將 proto 文件編譯成 golang 可識別的文件 【我是后來才創建spider 文件夾的,把spider.pb.go 和spider.proto 移到文件夾中】
輸出的目錄 proto所在目錄
protoc --go_out=plugins=grpc:./ ./spider.proto
需要注意的是,在本個 demo 中,客戶端與服務端都是
Golang
,所以在客戶端與服務端都公用一個 pb.go
模板文件(如果是不同的語言生成的pb是對應語言),可以將 pb.go
文件放置在雲上由雙方引用,也可以生成兩個副本放在兩端項目中,本次就使用 COPY
兩份的方式
由於 Golang
一個文件夾只有一個 package
,而生成的 pb.go
文件 package
為創建 proto
的名字(示例為 spider
), 所以我們在項目內單獨建立文件夾 spider
將文件放入其中即可正常使用
5.整個GPRC的代碼如下【我是服務端 和客戶端在一起的】
package main import ( "context" "fmt" "io/ioutil" "main/spider" "net" "net/http" "time" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) type server struct{} // 接收client端的請求,函數名需保持一致 // ctx參數必傳 // 參數二為自定義的參數,需從pb文件導入,因此pb文件必須可導入,文件放哪里隨意 // 返回值同參數二,為pb文件的返回結構體指針 func (s *server) GetAddressResponse(ctx context.Context, a *spider.SendAddress) (*spider.GetResponse, error) { // 邏輯寫在這里 switch a.Method { case "get", "Get", "GET": // 演示微服務用,故只寫get示例 status, body, err := get(a.Address) if err != nil { return nil, err } res := spider.GetResponse{ HttpCode: int32(status), Response: body, } return &res, nil } return nil, nil } func get(address string) (s int, r string, err error) { // get請求 resp, err := http.Get(address) if err != nil { return } defer resp.Body.Close() s = resp.StatusCode body, err := ioutil.ReadAll(resp.Body) if err != nil { return } r = string(body) return } func GPRCServer() { // 監聽本地端口 listener, err := net.Listen("tcp", "localhost:8080") if err != nil { return } s := grpc.NewServer() // 創建GRPC spider.RegisterGoSpiderServer(s, &server{}) // 在GRPC服務端注冊服務 reflection.Register(s) // 在GRPC服務器注冊服務器反射服務 // Serve方法接收監聽的端口,每到一個連接創建一個ServerTransport和server的grroutine // 這個goroutine讀取GRPC請求,調用已注冊的處理程序進行響應 err = s.Serve(listener) if err != nil { return } } func GPRCClient() { // 連接服務器 conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure()) if err != nil { fmt.Println(err) return } defer conn.Close() // 連接GRPC c := spider.NewGoSpiderClient(conn) // 創建要發送的結構體 req := spider.SendAddress{ Address: "http://www.baidu.com", Method: "get", } // 調用server的注冊方法 r, err := c.GetAddressResponse(context.Background(), &req) if err != nil { fmt.Println(err) return } // 打印返回值 fmt.Println(r) } func main() { go GPRCServer() time.Sleep(1000) go GPRCClient() var s string fmt.Scan(&s) }
3.修改GPRC程序 使用證書
package main import ( "context" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "main/spider" "net" "net/http" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" ) type server struct{} // 接收client端的請求,函數名需保持一致 // ctx參數必傳 // 參數二為自定義的參數,需從pb文件導入,因此pb文件必須可導入,文件放哪里隨意 // 返回值同參數二,為pb文件的返回結構體指針 func (s *server) GetAddressResponse(ctx context.Context, a *spider.SendAddress) (*spider.GetResponse, error) { // 邏輯寫在這里 switch a.Method { case "get", "Get", "GET": // 演示微服務用,故只寫get示例 status, body, err := get(a.Address) if err != nil { return nil, err } res := spider.GetResponse{ HttpCode: int32(status), Response: body, } return &res, nil } return nil, nil } func get(address string) (s int, r string, err error) { // get請求 resp, err := http.Get(address) if err != nil { return } defer resp.Body.Close() s = resp.StatusCode body, err := ioutil.ReadAll(resp.Body) if err != nil { return } r = string(body) return } func GPRCServer() { // 監聽本地端口 listener, err := net.Listen("tcp", "localhost:8080") if err != nil { return } //證書 cert, _ := tls.LoadX509KeyPair("server.pem", "server.key") certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("ca.pem") certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: certPool, }) s := grpc.NewServer(grpc.Creds(creds)) // 創建GRPC spider.RegisterGoSpiderServer(s, &server{}) // 在GRPC服務端注冊服務 reflection.Register(s) // 在GRPC服務器注冊服務器反射服務 // Serve方法接收監聽的端口,每到一個連接創建一個ServerTransport和server的grroutine // 這個goroutine讀取GRPC請求,調用已注冊的處理程序進行響應 err = s.Serve(listener) if err != nil { return } } func GPRCClient() { cert, _ := tls.LoadX509KeyPair("client.pem", "client.key") certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("ca.pem") certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ServerName: "localhost", RootCAs: certPool, }) // 連接服務器 conn, err := grpc.Dial("localhost:8080", grpc.WithTransportCredentials(creds)) if err != nil { fmt.Println(err) return } defer conn.Close() // 連接GRPC c := spider.NewGoSpiderClient(conn) // 創建要發送的結構體 req := spider.SendAddress{ Address: "http://www.baidu.com", Method: "get", } // 調用server的注冊方法 r, err := c.GetAddressResponse(context.Background(), &req) if err != nil { fmt.Println(err) return } // 打印返回值 fmt.Println(r) } func main() { go GPRCServer() time.Sleep(1000) go GPRCClient() var s string fmt.Scan(&s) }
--------------------------------------------------------------------------------------------------------------------
開啟grpc的雙向認證
受到 asp.ner core 5.0 Grpc雙向認證 和 restful api包裝 外加swagger啟用【VSCode創建】,決定多開一個 端口 用於grpc tsl的雙向認證, 修改后的server/main.go代碼如下:
package main import ( "crypto/tls" "crypto/x509" "fmt" "hello/gateway" pb "hello/protos" "hello/server/services" "io/ioutil" "log" "net" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" ) func main() { //grpc grpctslPort := ":9090" grpcPort := ":8081" httpPort := ":8080" ///grpc tsl 用於雙向認證 go GrpcTslServer(grpctslPort) ///普通的主要是便於gateway使用 lis, err := net.Listen("tcp", grpcPort) if err != nil { log.Fatalf("failed to listen: %v", err) } //gateway go gateway.HttpRun(grpcPort, httpPort) s := grpc.NewServer() pb.RegisterGreeterServer(s, services.NewServer()) fmt.Println("rpc services started, listen on localhost" + grpcPort) s.Serve(lis) } func GrpcTslServer(grpctslPort string) error { //證書 cert, a := tls.LoadX509KeyPair("../certs/server.pem", "../certs/server.key") if a != nil { fmt.Println(a) } certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("../certs/ca.pem") certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: certPool, }) sl := grpc.NewServer(grpc.Creds(creds)) // 創建GRPC pb.RegisterGreeterServer(sl, services.NewServer()) reflection.Register(sl) // 在GRPC服務器注冊服務器反射服務 listener, err := net.Listen("tcp", grpctslPort) if err != nil { fmt.Println(err) return err } fmt.Println("rpc tsl services started, listen on localhost" + grpctslPort) return sl.Serve(listener) }
client/main.go如下:
package main import ( "context" "crypto/tls" "crypto/x509" "encoding/json" "fmt" pb "hello/protos" "io/ioutil" "net/http" "strings" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { ///grpc client req := pb.HelloRequest{Name: "gavin"} cert, err := tls.LoadX509KeyPair("../certs/client.pem", "../certs/client.key") certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("../certs/ca.pem") certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ServerName: "localhost", RootCAs: certPool, }) // GRPC conn, err := grpc.Dial("localhost:9090", grpc.WithTransportCredentials(creds)) if err != nil { fmt.Println(err) return } defer conn.Close() //c := sv.NewServer() c := pb.NewGreeterClient(conn) r, err := c.SayHello(context.Background(), &req) if err != nil { fmt.Println(err) } // 打印返回值 fmt.Println(r) fmt.Println("http Start......................") //http requestByte, _ := json.Marshal(req) client := http.Client{Timeout: 15 * time.Second} resp, err := client.Post("http://localhost:8080/hello_world", "application/json", strings.NewReader(string(requestByte))) bodyBytes, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() fmt.Println(string(bodyBytes)) }
運行效果:
D:\GoProject\src\gogrpccertAndgateway\server>go run main.go rpc tsl services started, listen on localhost:9090 rpc services started, listen on localhost:8081 grpc-gateway listen on localhost:8080 request: gavin
D:\GoProject\src\gogrpccertAndgateway\client>go run main.go request: gavin message:"hello, gavin" http Start...................... {"message":"hello, gavin"}
源碼下載 https://download.csdn.net/download/dz45693/13985838 https://github.com/dz45693/gogrpccertAndgateway.git