原文地址:帶入gRPC:對 RPC 方法作自定義認證
項目地址:https://github.com/EDDYCJY/go...html
在前面的章節中,咱們介紹了兩種(證書算一種)可全局認證的方法:git
而在實際需求中,經常會對某些模塊的 RPC 方法作特殊認證或校驗。今天將會講解、實現這塊的功能點github
type PerRPCCredentials interface { GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) RequireTransportSecurity() bool }
在 gRPC 中默認定義了 PerRPCCredentials,它就是本章節的主角,是 gRPC 默認提供用於自定義認證的接口,它的做用是將所需的安全認證信息添加到每一個 RPC 方法的上下文中。其包含 2 個方法:golang
新建 simple_token_server/server.go 和 simple_token_client/client.go,目錄結構以下:segmentfault
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
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
所需的方法,關注兩點便可:安全
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 方法中進行認證檢查app
從新啓動 server.go 和 client.go,獲得如下結果:ui
$ go run client.go 2018/10/05 20:59:58 resp: gRPC Token Server
修改 client.go 的值,製造二者不一致,獲得無效結果:google
$ 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)code
本章節比較簡單,主要是針對 RPC 方法的自定義認證進行了介紹,若是是想作全局的,建議是觸類旁通從攔截器下手哦 😄