go學習筆記 Windows Go 1.15 以上版本的 GRPC 通信【自簽CA和雙向認證】


簡單說一下我的環境 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM