zoukankan      html  css  js  c++  java
  • 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 FilesOpenSSL-Win64inopenssl.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:GoProjectsrcgogrpccertAndgatewayserver>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:GoProjectsrcgogrpccertAndgatewayclient>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

  • 相关阅读:
    Rotation Kinematics
    离职 mark
    PnP 问题方程怎么列?
    DSO windowed optimization 代码 (4)
    Adjoint of SE(3)
    IMU 预积分推导
    DSO windowed optimization 代码 (3)
    DSO windowed optimization 代码 (2)
    OKVIS 代码框架
    DSO windowed optimization 代码 (1)
  • 原文地址:https://www.cnblogs.com/majiang/p/14212496.html
Copyright © 2011-2022 走看看