go-grpc实践指南-03认证

认证

grpc默认内置了两种认证方式

  • SSL/TLS认证方式
  • 基于Token的认证方式

同时,gRPC提供了接口用于扩展自定义认证方式

TLS认证示例-客户端、服务端双向认证

详细实现文档

Token认证示例

再进一步,继续扩展hello-tls项目,实现TLS + Token认证机制

目录结构:

示例代码
先修改客户端实现:client/main.go

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"io/ioutil"
	"log"
	"my_grpc/proto/hello"
)

const (
	OpenTLS = true
)

// 自定义认证
type customCredential struct {}
// GetRequestMetadata 实现自定义认证接口
func (c *customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  "101010",
		"appkey": "i am key",
	}, nil
}
// RequireTransportSecurity 自定义认证是否开启TLS
func (c customCredential) RequireTransportSecurity() bool {
	return OpenTLS
}

func main() {
	var opts []grpc.DialOption

	cert, err := tls.LoadX509KeyPair("keys/client/client.pem", "keys/client/client.key")
	if err != nil {
		log.Fatalln("tls.LoadX509 err:", err)
	}
	certPool := x509.NewCertPool()
	certBytes, err := ioutil.ReadFile("keys/ca/ca.crt")
	if err != nil {
		log.Fatalln("读取ca证书失败:", err)
	}
	certPool.AppendCertsFromPEM(certBytes)
	tcreds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},// 放入客户端证书
		ServerName: "localhost", //证书里面的 commonName
		RootCAs: certPool, // 证书池
	})
	creds := grpc.WithTransportCredentials(tcreds)
	opts = append(opts, creds)

	// 使用自定义认证
	opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))

	clientConn, err := grpc.Dial(":9000", opts...)
	if err != nil {
		log.Fatalln("grpc.Dial err:", err)
	}
	helloClient := hello.NewHelloClient(clientConn)
	reply, err := helloClient.SayHello(context.Background(), &hello.HelloRequest{Name: "gRPC"})
	if err != nil {
		log.Fatalln("helloClient.SayHello err:", err)
	}
	fmt.Println(reply)
}

这里我们定义了一个customCredential结构,并实现了两个方法GetRequestMetadata和RequireTransportSecurity。这是gRPC提供的自定义认证方式,每次RPC调用都会传输认证信息。customCredential其实是实现了grpc/credential包内的PerRPCCredentials接口。每次调用,token信息会通过请求的metadata传输到服务端。下面具体看一下服务端如何获取metadata中的信息。

修改server/main.go中的SayHello方法:

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
	"io/ioutil"
	"log"
	"my_grpc/proto/hello"
	"net"
)

type HelloService struct{
	hello.UnimplementedHelloServer
}

func (t *HelloService) SayHello(ctx context.Context, in *hello.HelloRequest) (*hello.HelloResponse, error) {
	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)
	}
	resp := new(hello.HelloResponse)
	resp.Message = fmt.Sprintf("Hello %s. Token info: appid=%s,appkey=%s", in.Name, appid, appkey)
	return resp, nil
}
func main() {
	// 使用tls进行加载key pair对
	certifacate, err := tls.LoadX509KeyPair("keys/server/server.pem", "keys/server/server.key")
	if err != nil {
		log.Fatalln("tls加载X509失败,err:",err)
	}
	// 创建证书池
	certPool := x509.NewCertPool()
	// 向证书池中加入证书
	certBytes, err := ioutil.ReadFile("keys/ca/ca.crt")
	if err != nil {
		log.Fatalln("读取ca.crt证书失败,err:", err)
	}
	// 加载证书从pem文件里面
	certPool.AppendCertsFromPEM(certBytes)
	// 创建credentials对象
	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{certifacate},  // 服务端证书
		ClientAuth: tls.RequireAndVerifyClientCert,  // 需要并且验证客户端证书
		ClientCAs: certPool,  // 客户端证书池
	})

	listen, _ := net.Listen("tcp", ":9000")
	gServer := grpc.NewServer(grpc.Creds(creds))
	hello.RegisterHelloServer(gServer, &HelloService{})
        fmt.Println("Listen ont 9000 with TLS + Token")
	_ = gServer.Serve(listen)
}

服务端可以从context中获取每次请求的metadata,从中读取客户端发送的token信息并验证有效性。

运行:

$ go run main.go
Listen on 50052 with TLS + Token

运行客户端程序 client/main.go:

$ go run main.go
// 认证成功结果
Hello gRPC
Token info: appid=101010,appkey=i am key
// 修改key验证认证失败结果:
rpc error: code = 16 desc = Token认证信息无效: appID=101010, appKey=i am not key

至此我们完成了grpc的TLS客户端与服务器的双向认证以及自定义的Token认证。(双认证同时使用)

Token认证参考文档

posted @ 2022-09-23 17:36  专职  阅读(134)  评论(0编辑  收藏  举报