gRPC入门指南 — 自定义认证(六)

Go语言精选

共 6065字,需浏览 13分钟

 ·

2021-08-29 07:28

前言

在前面的章节(文末推荐阅读)中,我们通过 TLS 证书的方式对通信数据进行了加密。另外,我们还可以给 RPC 方法添加自定义的验证方法,使得数据更加安全。这篇文章我们就以 Token 认证为例,介绍 gRPC 如何添加自定义验证方法。

自定义认证

gRPC 官方默认提供了用于自定义认证的接口,作用是将所需的安全认证信息添加到 RPC 方法的上下中。

type PerRPCCredentials interface {
 GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
 RequireTransportSecurity() bool
}
  • GetRequestMetadata() 获取当前请求认证所需的元数据;
  • RequireTransportSecurity() 是否需要基于 TLS 认证进行安全传输;

接下来我们自定义 token,实现这两个方法:

type Token struct {
 AppId     string
 AppSecret string
}

// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
 return map[string]string{"app_id": t.AppId, "app_secret": t.AppSecret}, nil
}

// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
 return false
}

Server 端

完整的代码如下:

package main

import (
 "context"
 pb "go-grpc-example/6-rpc_auth/proto"
 "google.golang.org/grpc"
 "google.golang.org/grpc/codes"
 "google.golang.org/grpc/metadata"
 "google.golang.org/grpc/status"
 "log"
 "net"
)

const (
 Address string = ":8000"
 Network string = "tcp"
)

type SimpleService struct{}

func (s *SimpleService) GetSimpleInfo(ctx context.Context, req *pb.SimpleRequest) (*pb.SimpleResponse, error) {
 // 检测Token是否有效
 if err := check(ctx); err != nil {
  return nil, err
 }
 data := req.Data
 log.Printf("get from client: %v", data)

 resp := pb.SimpleResponse{
  Code:  1,
  Value: "gRPC",
 }
 return &resp, nil
}

func main() {

 listener, err := net.Listen(Network, Address)
 if err != nil {
  log.Fatalf("net.listen err: %v", err)
 }
 log.Println(Address, " net listening...")

 grpcServer := grpc.NewServer()

 pb.RegisterSimpleServer(grpcServer, &SimpleService{})

 err = grpcServer.Serve(listener)
 if err != nil {
  log.Fatalf("grpc server err: %v", err)
 }
}

func check(ctx context.Context) error {
 // 从上下文章获取元数据
 md, ok := metadata.FromIncomingContext(ctx)
 if !ok {
  return status.Errorf(codes.Unauthenticated, "获取token失败")
 }

 var (
  appId     string
  appSecret string
 )
 if value, ok := md["app_id"]; ok {
  appId = value[0]
 }
 if value, ok := md["app_secret"]; ok {
  appSecret = value[0]
 }
 if appId != "grpc_auth" || appSecret != "123456" {
  return status.Errorf(codes.Unauthenticated, "Token无效,appId:%v,appSecret:%v", appId, appSecret)
 }
 return nil
}

上面的代码,在 GetSimpleInfo() 方法中,在处理实际的业务逻辑之前,我们调用了 check() 函数验证 token 信息是否正确。

Client 端

完整的代码如下:

package main

import (
 "context"
 pb "go-grpc-example/5-security/proto"
 "google.golang.org/grpc"
 "log"
)

const Address = ":8000"

type Token struct {
 AppId     string
 AppSecret string
}

// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
 return map[string]string{"app_id": t.AppId, "app_secret": t.AppSecret}, nil
}

// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
 return false
}

func main() {

 // Token token认证
 token := Token{
  AppId:     "grpc_auth",
  AppSecret: "123456",
 }

 opts := []grpc.DialOption{
  grpc.WithInsecure(),
  grpc.WithPerRPCCredentials(&token),
 }

 conn, err := grpc.Dial(Address, opts...)
 if err != nil {
  log.Fatalf("dial conn err: %v", err)
 }
 defer conn.Close()

 grpcClient := pb.NewSimpleClient(conn)

 req := pb.SimpleRequest{
  Data: "seekload",
 }
 resp, err := grpcClient.GetSimpleInfo(context.Background(), &req)
 if err != nil {
  log.Fatalf("resp err: %v", err)
 }
 log.Printf("get from client,code: %v,value: %v", resp.Code, resp.Value)

}

上面的代码,自定义 token,实现了 gRPC 提供的两个方法。在客户端中调用 Dial() 时添加自定义验证方法。

grpc.WithPerRPCCredentials(&token)

验证

分别运行服务端和客户端,输出:

go run server.go
:8000  net listening...
get from client: seekload

go run client.go
get from client,code: 1,value: gRPC

可以制造 token 信息不正确,客户端返回错误:

resp err: rpc error: code = Unauthenticated desc = Token无效,appId:grpc_auth,appSecret:123457

思考

大家试想下,实际业务中肯定不止一个 RPC 方法,每个方法中都需要手动加一段验证 token 信息的代码,这样岂不是很繁琐。那有没有“一处验证,处处验证”的方法?答案肯定是有的,对!拦截器,这就是我们下一篇文章给大家介绍的。



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报