Grpc使用实践总结 - Go语言中文社区

Grpc使用实践总结


1.获取Grpc客户端的IP

golang客户端发给服务端的http请求,本质上就是一个Request的结构体(见net/http/request.go) 中除了包含header、body外还包含其他的附加信息,比如RemoteAddr(客户端的地址) 。这样http很容易就可以获取客户端的地址,详细解释如下:。

    // RemoteAddr allows HTTP servers and other software to record
    // the network address that sent the request, usually for
    // logging. This field is not filled in by ReadRequest and
    // has no defined format. The HTTP server in this package
    // sets RemoteAddr to an "IP:port" address before invoking a
    // handler.
    // This field is ignored by the HTTP client.
    RemoteAddr string

那么使用grpc-go是否能够获取客户端的地址呢?

答案是肯定的,笔者查了下grpc的源码,发下peer包中包含了Addr(grpc/peer/peer.go),定义如下:

// Package peer defines various peer information associated with RPCs and
// corresponding utils.
package peer

import (
    "net"

    "golang.org/x/net/context"
    "google.golang.org/grpc/credentials"
)

// Peer contains the information of the peer for an RPC, such as the address
// and authentication information.
type Peer struct {
    // Addr is the peer address.
    Addr net.Addr
    // AuthInfo is the authentication information of the transport.
    // It is nil if there is no transport security being used.
    AuthInfo credentials.AuthInfo
}

type peerKey struct{}

// NewContext creates a new context with peer information attached.
func NewContext(ctx context.Context, p *Peer) context.Context {
    return context.WithValue(ctx, peerKey{}, p)
}

// FromContext returns the peer information in ctx if it exists.
func FromContext(ctx context.Context) (p *Peer, ok bool) {
    p, ok = ctx.Value(peerKey{}).(*Peer)
    return
}

grpc的请求中默认都会有context的值,我们可以使用FromContext的方法获取Peer结构,最终取出客户端的ip地址。以下是笔者在自己的项目中使用获取的方式,仅供参考:

func getClietIP(ctx context.Context) (string, error) {
    pr, ok := peer.FromContext(ctx)
    if !ok {
        return "", fmt.Errorf("[getClinetIP] invoke FromContext() failed")
    }
    if pr.Addr == net.Addr(nil) {
        return "", fmt.Errorf("[getClientIP] peer.Addr is nil")
    }
    addSlice := strings.Split(pr.Addr.String(), ":")
    return addSlice[0], nil
}

注意:在使用grpc stream的方式中,context的值可以直接从stream中获取,方法stream.Context()

2.Grpc双向验证

笔者在自己的项目中使用grpc的双向验证功能,此处使用的证书均为笔者自签获取。笔者的双向验证流程如下所示:

  • rootB.crt作为根证书签发testB.crt的二级证书

  • rootA.crt作为根证书签发testA.crt的二级证书

    注意: testB.key和testA.key保存用户的私钥不会再网络上传输,笔者在理解双向验证时,是基于HTTPS的单向验证进行理解的。

    HTTPS的单向验证理解:

    • 浏览器会先预先植入一些可信网站的根证书,用户也可自行下载可信网站的根证书。
    • 浏览器在使用https连接server端服务时,server端会首先将证书通过网络传输发送到浏览器(客户端)。
    • 浏览器(客户端)在接收到server的证书后,验证server端的证书是否为自己签发的子证书。

    双向验证

client端双向验证代码

    certificate, err := tls.LoadX509KeyPair(config.SelfCertFile, config.SelfKeyFile)
    if err != nil {
        log.Fatalf("load x509 key pair failed: %s", err)
    }
    certPool := x509.NewCertPool()
    bs, err := ioutil.ReadFile(config.RootCAFile)
    if err != nil {
        log.Fatalf("fail to read ca cert: %s", err)
    }
    ok := certPool.AppendCertsFromPEM(bs)
    if !ok {
        log.Fatal("failed to append certs")
    }

    transportCreds := credentials.NewTLS(&tls.Config{  //核心产生一个传输通道的证书
        ServerName:   config.ServerName,
        Certificates: []tls.Certificate{certificate},
        RootCAs:      certPool,
    })
    dialOption := grpc.WithTransportCredentials(transportCreds) //配置一个连接层的安全证书
    grpcConn, err := grpc.Dial(grpcAddress, dialOption) //与grpc的server端建立连接
    if err != nil {
        log.WithError(err).Fatal("[NewReviewClient] create grpc conn failed")
    }
    ...
  • 主要使用的函数:`func WithTransportCredentials(creds credentials.TransportCredentials) DialOption
  • grpc还提供针对每个连接的访问授权WithPerRPCCredentials()

Server端的双向验证代码

    caCert, err := ioutil.ReadFile(remoteCAFile)
    if err != nil {
        log.WithError(err).Fatal("Fail to load client root ca certs")
    }
    caCertPool := x509.NewCertPool()
    ok := caCertPool.AppendCertsFromPEM(caCert)
    if !ok {
        log.Fatal("Failed to append client certs")
    }

    certificate, err := tls.LoadX509KeyPair(localCrtFile, localKeyFile)

    tlsConfig := &tls.Config{
        ClientAuth:   tls.RequireAndVerifyClientCert,
        Certificates: []tls.Certificate{certificate},
        ClientCAs:    caCertPool,
    }

    svrOption :=  grpc.Creds(credentials.NewTLS(tlsConfig)) 
    //Creds函数返回一个已经包含证书的server端连接ServerOption结构
    ...
  • 主要使用的函数:func Creds(c credentials.TransportCredentials) ServerOption

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/phantom_111/article/details/78797502
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-25 15:29:01
  • 阅读 ( 2681 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢