前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang笔记 6.3.1 gRPC 使用 metadata 自定义认证

Golang笔记 6.3.1 gRPC 使用 metadata 自定义认证

作者头像
twowinter
发布2020-04-17 17:58:24
5K1
发布2020-04-17 17:58:24
举报
文章被收录于专栏:twowintertwowinter

1 背景介绍

在 http 请求当中我们可以设置 header 用来传递数据,grpc 底层采用 http2 协议也是支持传递数据的,采用的是 metadata。 Metadata 对于 gRPC 本身来说透明, 它使得 client 和 server 能为对方提供本次调用的信息。就像一次 http 请求的 RequestHeader 和 ResponseHeader,http header 的生命周期是一次 http 请求, Metadata 的生命周期则是一次 RPC 调用。

2 用法介绍

2.1 客户端请求中携带 metadata

方法介绍

在 go 语言中,可以用 grpc.WithPerRPCCredentials 方法来实现。

代码语言:javascript
复制
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption

WithPerRPCCredentials returns a DialOption which sets credentials and places auth state on each outbound RPC.

通常来说,认证信息是需要每次都携带,但如果需要单次携带 metadata,可以使用 metadata.NewOutgoingContext 方法来创建一个携带 metadata 的 context。

Step1. 实例准备

方法的入参是 PerRPCCredentials 接口,因此需要准备一个实例,实现接口的方法。

代码语言:javascript
复制
type PerRPCCredentials interface {
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
	RequireTransportSecurity() bool
}

Step2. 通过 DialOption 新建客户端

代码语言:javascript
复制
    opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
    conn, err := grpc.Dial(address, opts...)

2.2 服务端中解析 metadata

从 RPC 消息的上下文中获取 metadata

代码语言:javascript
复制
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
    md, ok := metadata.FromIncomingContext(ctx)
    // do something with metadata
}

3 hello world 示例修改

3.1 客户端

代码语言:javascript
复制
// customCredential 自定义认证
type customCredential struct{}

func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{
        "appid":  "101010",
        "appkey": "i am key",
    }, nil
}

func (c customCredential) RequireTransportSecurity() bool {
    return false
}
// 上面是第1步,实例准备


func main() {
    var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	opts = append(opts, grpc.WithBlock())
    // 使用自定义认证
    opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
	// Set up a connection to the server.
	conn, err := grpc.Dial(address, opts...)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	// Contact the server and print out its response.
	name := defaultName
	if len(os.Args) > 1 {
		name = os.Args[1]
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}

3.2 服务端

服务端使用了 metadata 一个的关键函数,它可从 context 中取出 metadata (map 类型):

代码语言:javascript
复制
// FromIncomingContext returns the incoming metadata in ctx if it exists.  The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to copies of the returned MD.
func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
	md, ok = ctx.Value(mdIncomingKey{}).(MD)
	return
}

具体处理如下:

代码语言:javascript
复制
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    // 解析metada中的信息并验证
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return nil, grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
	}
	
    var (
        appid  string
        appkey string
    )

    if val, ok := md["appid"]; ok {
        appid = val[0]
    }

    if val, ok := md["appkey"]; ok {
        appkey = val[0]
    }

    if appid != "101010" || appkey != "i am key" {
        return nil, grpc.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
    }
	log.Printf("Received: %v.\nToken info: appid=%s,appkey=%s", in.GetName(), appid, appkey)
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

3.3 运行效果

Server 端打印如下信息:

代码语言:javascript
复制
# go run greeter_server/main.go
2019/11/13 12:03:21 Received: world.
Token info: appid=101010,appkey=i am key

4 小结

gRPC 可用 metadata 自定义认证信息。客户端使用 WithPerRPCCredentials 方法,服务端使用 metadata.FromIncomingContext 方法从 RPC 消息的上下文中获取 metadata。

END

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 背景介绍
  • 2 用法介绍
    • 2.1 客户端请求中携带 metadata
      • 2.2 服务端中解析 metadata
      • 3 hello world 示例修改
        • 3.1 客户端
          • 3.2 服务端
            • 3.3 运行效果
            • 4 小结
            • END
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档