Go gRPC 系列八:对 RPC 方法做自定义认证

煎鱼啊 · · 1225 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

大家好,我是煎鱼,在前面的章节中,我们介绍了两种(证书算一种)可全局认证的方法:

  1. TLS 证书认证
  2. 基于 CA 的 TLS 证书认证
  3. Unary and Stream interceptor

而在实际需求中,常常会对某些模块的 RPC 方法做特殊认证或校验。今天将会讲解、实现这块的功能点

课前知识

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

在 gRPC 中默认定义了 PerRPCCredentials,它就是本章节的主角,是 gRPC 默认提供用于自定义认证的接口,它的作用是将所需的安全认证信息添加到每个 RPC 方法的上下文中。其包含 2 个方法:

  • GetRequestMetadata:获取当前请求认证所需的元数据(metadata)
  • RequireTransportSecurity:是否需要基于 TLS 认证进行安全传输

目录结构

新建 simple_token_server/server.go 和 simple_token_client/client.go,目录结构如下:

go-grpc-example ├── client │   ├── simple_client │   ├── simple_http_client │   ├── simple_token_client │   └── stream_client ├── conf ├── pkg ├── proto ├── server │   ├── simple_http_server │   ├── simple_server │   ├── simple_token_server │   └── stream_server └── vendor 复制代码

gRPC

Client

package main import ( "context" "log" "google.golang.org/grpc" "github.com/EDDYCJY/go-grpc-example/pkg/gtls"	pb "github.com/EDDYCJY/go-grpc-example/proto" ) const PORT = "9004" type Auth struct {	AppKey string	AppSecret string } func (a *Auth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{"app_key": a.AppKey, "app_secret": a.AppSecret}, nil } func (a *Auth) RequireTransportSecurity() bool { return true } func main() {	tlsClient := gtls.Client{	ServerName: "go-grpc-example",	CertFile: "../../conf/server/server.pem",	}	c, err := tlsClient.GetTLSCredentials() if err != nil {	log.Fatalf("tlsClient.GetTLSCredentials err: %v", err)	}	auth := Auth{	AppKey: "eddycjy",	AppSecret: "20181005",	}	conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c), grpc.WithPerRPCCredentials(&auth))	... } 复制代码

在 Client 端,重点实现 type PerRPCCredentials interface 所需的方法,关注两点即可:

  • struct Auth:GetRequestMetadata、RequireTransportSecurity
  • grpc.WithPerRPCCredentials

Server

package main import ( "context" "log" "net" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "github.com/EDDYCJY/go-grpc-example/pkg/gtls"	pb "github.com/EDDYCJY/go-grpc-example/proto" ) type SearchService struct {	auth *Auth } func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { if err := s.auth.Check(ctx); err != nil { return nil, err	} return &pb.SearchResponse{Response: r.GetRequest() + " Token Server"}, nil } const PORT = "9004" func main() {	... } type Auth struct {	appKey string	appSecret string } func (a *Auth) Check(ctx context.Context) error {	md, ok := metadata.FromIncomingContext(ctx) if !ok { return status.Errorf(codes.Unauthenticated, "自定义认证 Token 失败")	}	var (	appKey string	appSecret string	) if value, ok := md["app_key"]; ok {	appKey = value[0]	} if value, ok := md["app_secret"]; ok {	appSecret = value[0]	} if appKey != a.GetAppKey() || appSecret != a.GetAppSecret() { return status.Errorf(codes.Unauthenticated, "自定义认证 Token 无效")	} return nil } func (a *Auth) GetAppKey() string { return "eddycjy" } func (a *Auth) GetAppSecret() string { return "20181005" } 复制代码

在 Server 端就更简单了,实际就是调用 metadata.FromIncomingContext 从上下文中获取 metadata,再在不同的 RPC 方法中进行认证检查

验证

重新启动 server.go 和 client.go,得到以下结果:

$ go run client.go 2018/10/05 20:59:58 resp: gRPC Token Server 复制代码

修改 client.go 的值,制造两者不一致,得到无效结果:

$ go run client.go 2018/10/05 21:00:05 client.Search err: rpc error: code = Unauthenticated desc = invalid token exit status 1 复制代码

一个个加太麻烦

我相信你肯定会问一个个加,也太麻烦了吧?有这个想法的你,应当把 type PerRPCCredentials interface 做成一个拦截器(interceptor)

总结

本章节比较简单,主要是针对 RPC 方法的自定义认证进行了介绍,如果是想做全局的,建议是举一反三从拦截器下手哦。

如果有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见,如果喜欢或对你有所帮助,欢迎 Star,对作者是一种鼓励和推进。

我的公众号

image

参考

本系列示例代码


有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:煎鱼啊

查看原文:Go gRPC 系列八:对 RPC 方法做自定义认证

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1225 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传