【GoLang 那點事】gRPC 認證的多種方式實踐(五)

a_wei發表於2019-09-08

gRPC安全認證介紹

  • gRPC被設計成可以利用外掛的形式支援多種授權認證機制,你可以採用自己喜歡的,簡單的,認為方便的一種方式,選擇權在使用者手裡
  • 支援的授權認證機制如下
    1. SSL/TLS認證
    2. 自定義Token認證
  • SSL/TLS的概念可以參考下面的文章
  • https://www.techug.com/post/https-ssl-tls....

SSL/TLS認證方式

  • 首先通過openssl生成證書和私鑰,命令如下
//生成私鑰
openssl genrsa -out server.key 2048

//生成證書
openssl req -new -x509 -sha256 -key server.key  -out server.crt -days 36500

//按照提示輸入如下資訊
國家名稱
Country Name (2 letter code) [AU]:
//省名稱
State or Province Name (full name) [Some-State]:
//城市名稱
Locality Name (eg, city) []:
//理解為公司名稱
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
//理解為你所在部門的名稱
Organizational Unit Name (eg, section) []:
//你的伺服器名稱(網站名稱)
Common Name (e.g. server FQDN or YOUR name) []:
//聯絡郵箱
Email Address []:
  • 服務端如下
func StartServer() {

   lis, err := net.Listen("tcp", "127.0.0.1:8090")
   if err != nil {
      log.Fatalf("failed to listen: %v", err)
   }
   // TLS認證
   // 兩個入參分別是 (certFile, keyFile string)
   // 自簽名證書檔案和私鑰檔案
   creds, err := credentials.NewServerTLSFromFile("cert", "key")
   //建立grpcServer傳入證書 
   gRpcServer := grpc.NewServer(grpc.Creds(creds))
   pb.RegisterHelloServiceServer(gRpcServer, &HelloServiceServer{})
   gRpcServer.Serve(lis)
}
  • 客戶端如下

func StartClient() {
   // TLS認證
   creds, err := credentials.NewClientTLSFromFile("cert", "ServerName")
   //連線伺服器 
   conn, err := grpc.Dial("127.0.0.1:8090",grpc.WithTransportCredentials(creds) )
   if err != nil{
      fmt.Println(err)
      return
   }
   c := pb.NewHelloServiceClient(conn)
   //
   ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   defer cancel()
   r, err := c.HelloWorldClientAndServerStream(ctx, grpc.EmptyCallOption{})
   if err != nil {
        log.Fatalf("%v", err)
        return
   }
   r.Send(&pb.HelloRequest{Request: "my is golang gRpc client "})
   r.CloseSend()
}
  • 如上就開啟了gRPC的TLS認證

Token認證

  • 我們先看一個gRPC提供我們的一個介面,這個介面終有兩個方法,介面位於credentials
    包下,這個介面需要客戶端來實現
  • 第一個方法作用是獲取後設資料資訊,也就是客戶端提供的key,value對,context用於控制超時和取消,uri是請求入口處的uri
  • 第二個方法的作用是否需要基於 TLS 認證進行安全傳輸是,如果返回值是true,則必須加上TLS驗證,返回值是false則不用
type PerRPCCredentials interface {
    GetRequestMetadata(ctx context.Context, uri ...string) 
        (map[string]string, error)
    RequireTransportSecurity() bool
}
  • 客戶端端實現介面,程式碼如下
//自定義token認證
type CustomerTokenAuth struct {
}
//獲取後設資料
func (c CustomerTokenAuth) GetRequestMetadata(ctx context.Context, 
   uri...string) (map[string]string, error) {

   return map[string]string{
      "appId":  "master",
      "appkey": "1aqfs5g456j",
   }, nil
}
//是否開啟傳輸安全 TLS
func (c CustomerTokenAuth) RequireTransportSecurity() bool {
   return false
}
  • 客戶端按照如下方式使用

var opts []grpc.DialOption
//grpc.WithInsecure()這個是一定要新增的,代表開啟安全的選項
opts =append(opts,grpc.WithInsecure())
//新增自定義token驗證
opts = append(opts,grpc.WithPerRPCCredentials(new(CustomerTokenAuth)))
//連線服務端
conn, err := grpc.Dial("127.0.0.1:8090",opts...)
  • 服務端按照如下方式校驗,當然我們也可以使用攔截器的形式對每個方法進行攔截,而不是像如下在每個方法中硬編碼是的。

type HelloServiceServer struct {
}
//這是服務端實現的一個方法
func (*HelloServiceServer2) HelloWorld(ctx context.Context, 
   req *pb.HelloRequest) (*pb.HelloResponse, error) {
   //獲取後設資料資訊
   md,ok := metadata.FromIncomingContext(ctx)
   if !ok {
      return nil,errors.New("未傳輸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 != "123" || appkey != "456" {
        return nil, errors.New("token傳輸不正確")
   }
   return &pb.HelloResponse{Response: "hello my is gRpcServer"}, nil
}

總結

  • gRPC將各種認證方式濃縮統一到一個憑證(credentials)上,可以單獨使用一種憑證,比如只使用TLS憑證或者只使用自定義憑證,也可以多種憑證組合,gRPC提供統一的API驗證機制,使研發人員使用方便,這也是gRPC設計的巧妙之處

歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來

圖片

那小子阿偉

相關文章