<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          gRPC入門指南 — 自定義認(rèn)證(六)

          共 6065字,需瀏覽 13分鐘

           ·

          2021-08-29 07:28

          前言

          在前面的章節(jié)(文末推薦閱讀)中,我們通過(guò) TLS 證書的方式對(duì)通信數(shù)據(jù)進(jìn)行了加密。另外,我們還可以給 RPC 方法添加自定義的驗(yàn)證方法,使得數(shù)據(jù)更加安全。這篇文章我們就以 Token 認(rèn)證為例,介紹 gRPC 如何添加自定義驗(yàn)證方法。

          自定義認(rèn)證

          gRPC 官方默認(rèn)提供了用于自定義認(rèn)證的接口,作用是將所需的安全認(rèn)證信息添加到 RPC 方法的上下中。

          type PerRPCCredentials interface {
           GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
           RequireTransportSecurity() bool
          }
          • GetRequestMetadata() 獲取當(dāng)前請(qǐng)求認(rèn)證所需的元數(shù)據(jù);
          • RequireTransportSecurity() 是否需要基于 TLS 認(rèn)證進(jìn)行安全傳輸;

          接下來(lái)我們自定義 token,實(shí)現(xiàn)這兩個(gè)方法:

          type Token struct {
           AppId     string
           AppSecret string
          }

          // GetRequestMetadata 獲取當(dāng)前請(qǐng)求認(rèn)證所需的元數(shù)據(jù)
          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 認(rèn)證進(jìn)行安全傳輸
          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) {
           // 檢測(cè)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 {
           // 從上下文章獲取元數(shù)據(jù)
           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無(wú)效,appId:%v,appSecret:%v", appId, appSecret)
           }
           return nil
          }

          上面的代碼,在 GetSimpleInfo() 方法中,在處理實(shí)際的業(yè)務(wù)邏輯之前,我們調(diào)用了 check() 函數(shù)驗(yàn)證 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 獲取當(dāng)前請(qǐng)求認(rèn)證所需的元數(shù)據(jù)
          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 認(rèn)證進(jìn)行安全傳輸
          func (t *Token) RequireTransportSecurity() bool {
           return false
          }

          func main() {

           // Token token認(rèn)證
           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,實(shí)現(xiàn)了 gRPC 提供的兩個(gè)方法。在客戶端中調(diào)用 Dial() 時(shí)添加自定義驗(yàn)證方法。

          grpc.WithPerRPCCredentials(&token)

          驗(yàn)證

          分別運(yùn)行服務(wù)端和客戶端,輸出:

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

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

          可以制造 token 信息不正確,客戶端返回錯(cuò)誤:

          resp err: rpc error: code = Unauthenticated desc = Token無(wú)效,appId:grpc_auth,appSecret:123457

          思考

          大家試想下,實(shí)際業(yè)務(wù)中肯定不止一個(gè) RPC 方法,每個(gè)方法中都需要手動(dòng)加一段驗(yàn)證 token 信息的代碼,這樣豈不是很繁瑣。那有沒(méi)有“一處驗(yàn)證,處處驗(yàn)證”的方法?答案肯定是有的,對(duì)!攔截器,這就是我們下一篇文章給大家介紹的。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 19
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色精品 | 精品人妻无码一区二区三区竹菊影视 | 老司机福利在线视频 | 国产精品日产欧美 | 久久国产精品国产色婷婷 |