Golang gRPC實踐 連載五 攔截器 Interceptor

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Interceptor

grpc服務端提供了interceptor功能,可以在服務端接收到請求時優先對請求中的資料做一些處理後再轉交給指定的服務處理並響應,功能類似middleware,很適合在這裡處理驗證、日誌等流程。

在自訂Token認證的樣本中,認證資訊是由每個服務中的方法處理並認證的,如果有大量的介面方法,這種姿勢就太蛋疼了,每個介面實現都要先處理認證資訊。這個時候interceptor就站出來解決了這個問題,可以在請求被轉到具體介面之前處理認證資訊,一處認證,到處無憂,看代碼吧,修改hello-token項目的服務端實現:

server/main.go

package mainimport (    "net"    pb "github.com/Jergoo/go-grpc-example/proto"    "golang.org/x/net/context"    "google.golang.org/grpc"    "google.golang.org/grpc/codes"       // grpc 響應狀態代碼    "google.golang.org/grpc/credentials" // grpc認證包    "google.golang.org/grpc/grpclog"    "google.golang.org/grpc/metadata" // grpc metadata包)const (    // Address gRPC服務地址    Address = "127.0.0.1:50052")// 定義helloService並實現約定的介面type helloService struct{}// HelloService ...var HelloService = helloService{}func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {    resp := new(pb.HelloReply)    resp.Message = "Hello " + in.Name + "."    return resp, nil}// auth 驗證Tokenfunc auth(ctx context.Context) error {    md, ok := metadata.FromContext(ctx)    if !ok {        return 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 grpc.Errorf(codes.Unauthenticated, "Token認證資訊無效: appid=%s, appkey=%s", appid, appkey)    }    return nil}func main() {    listen, err := net.Listen("tcp", Address)    if err != nil {        grpclog.Fatalf("Failed to listen: %v", err)    }    var opts []grpc.ServerOption    // TLS認證    creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")    if err != nil {        grpclog.Fatalf("Failed to generate credentials %v", err)    }    opts = append(opts, grpc.Creds(creds))    // 註冊interceptor    var interceptor grpc.UnaryServerInterceptor    interceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {        err = auth(ctx)        if err != nil {            return        }        // 繼續處理請求        return handler(ctx, req)    }    opts = append(opts, grpc.UnaryInterceptor(interceptor))    // 執行個體化grpc Server    s := grpc.NewServer(opts...)    // 註冊HelloService    pb.RegisterHelloServer(s, HelloService)    grpclog.Println("Listen on " + Address + " with TLS + Token + Interceptor")    s.Serve(listen)}

運行:

go run main.goListen on 50052 with TLS + Token

client/main.go

package mainimport (    pb "github.com/Jergoo/go-grpc-example/proto" // 引入proto包    "golang.org/x/net/context"    "google.golang.org/grpc"    "google.golang.org/grpc/credentials" // 引入grpc認證包    "google.golang.org/grpc/grpclog")const (    // Address gRPC服務地址    Address = "127.0.0.1:50052"    // OpenTLS 是否開啟TLS認證    OpenTLS = true)// 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 {    if OpenTLS {        return true    }    return false}func main() {    var err error    var opts []grpc.DialOption    if OpenTLS {        // TLS串連        creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")        if err != nil {            grpclog.Fatalf("Failed to create TLS credentials %v", err)        }        opts = append(opts, grpc.WithTransportCredentials(creds))    } else {        opts = append(opts, grpc.WithInsecure())    }    // 指定自訂認證    opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))    conn, err := grpc.Dial(Address, opts...)    if err != nil {        grpclog.Fatalln(err)    }    defer conn.Close()    // 初始化用戶端    c := pb.NewHelloClient(conn)    // 調用方法    reqBody := new(pb.HelloRequest)    reqBody.Name = "gRPC"    r, err := c.SayHello(context.Background(), reqBody)    if err != nil {        grpclog.Fatalln(err)    }    grpclog.Println(r.Message)}

運行用戶端程式 client/main.go:

go run main.go// 認證成功結果Hello gRPCToken info: appid=101010,appkey=i am key// 認證失敗結果:rpc error: code = 16 desc = Token認證資訊無效: appID=101010, appKey=i am not key

運行結果和hello-token項目一樣,簡單不,只需要在執行個體化server前註冊需要的interceptor,就可以輕鬆解決那個蛋疼的問題,想註冊幾個就註冊幾個。

項目推薦:

go-grpc-middleware

這個項目對interceptor進行了封裝,支援多個攔截器的鏈式組裝,對於需要多種處理的地方使用起來會更方便些。

參考

本系列範例程式碼

  • go-grpc-example

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.