RPC 編程 -五-gRPC 中的 Token 認證

  1. 介紹

gRPC爲每個gRPC方法調用提供了Token認證支持,可以基於用戶傳入的Token判斷用戶是否登陸、以及權限等...,實現Token認證的前提是,需要定義一個結構體,並實現grpc.PerRPCCredentials接口。

1.1 grpc.PerRPCCredentials

type PerRPCCredentials interface {
 // 返回需要認證的必要信息
 GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
  // 是否使用安全鏈接(TLS)
 RequireTransportSecurity() bool
}
  1. 步驟梳理

  1. 代碼實現

3.1 編寫proto文件

文件: ./proto/token.proto

syntax = "proto3";

package tokenservice;
option go_package = "server/tokenservice";

// 驗證參數
message TokenValidateParam {
  string token = 1;
  int32 uid = 2;
}

// 請求參數
message Request {
  string name = 1;
}

// 請求返回
message Response {
  int32 uid = 1;
  string name = 2;
}

// 服務
service TokenService{
  rpc Test(Request) returns (Response);
}

3.2 生成Go代碼

$ protoc --go_out=.  --go-grpc_out=. proto/*

3.3 實現grpc.PerRPCCredentials

文件: ./server/tokenservice/token_grpc.pb.go

/**
 * @Description: 返回token信息
 * @Author: LiuQHui
 * @Receiver x
 * @Param ctx
 * @Param uri
 * @Return map[string]string
 * @Return error
 * @Date 2022-02-21 15:40:44
 */
func (x *TokenValidateParam) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
 return map[string]string{
  "uid":  strconv.FormatInt(int64(x.GetUid()),10),
  "token": x.GetToken(),
 }, nil
}

/**
 * @Description: 自定義認證是否開啓TLS
 * @Author: LiuQHui
 * @Receiver x
 * @Return bool
 * @Date 2022-02-21 15:40:24
 */
func (x *TokenValidateParam) RequireTransportSecurity() bool {
 return false
}

3.4 編寫驗證token方法

文件: ./server/tokenservice/token.go

package tokenservice

import (
 "context"
 "crypto/md5"
 "fmt"
 "google.golang.org/grpc/codes"
 "google.golang.org/grpc/metadata"
 "google.golang.org/grpc/status"
)

// 定義token
type TokenAuth struct{}

func (t TokenAuth) CheckToken(ctx context.Context) (*Response, error) {
 // 驗證token
 md, b := metadata.FromIncomingContext(ctx)
 if !b {
  return nil, status.Error(codes.InvalidArgument, "token信息不存在")
 }
 var token, uid string
 // 取出token
 tokenInfo, ok := md["token"]
 if !ok {
  return nil, status.Error(codes.InvalidArgument, "token不存在")
 }
 token = tokenInfo[0]
 // 取出uid
 uidTmp, ok := md["uid"]
 if !ok {
  return nil, status.Error(codes.InvalidArgument, "uid不存在")
 }
 uid = uidTmp[0]
 // 驗證
 sum := md5.Sum([]byte(uid))
 md5Str := fmt.Sprintf("%x", sum)
 if md5Str != token {
  fmt.Println("md5Str:", md5Str)
  fmt.Println("password:", token)
  return nil, status.Error(codes.InvalidArgument, "token驗證失敗")
 }
 return nil, nil
}

3.5 實現被調用的方法Test

文件: ./server/tokenservice/token_grpc.pb.go

// 匿名TokenAuth,可以直接調用驗證token方法
type UnimplementedTokenServiceServer struct {
 TokenAuth
}
/**
 * @Description: 實現Test方法(生成的代碼,默認不實現具體邏輯)
 * @Author: LiuQHui
 * @Receiver u
 * @Param ctx
 * @Param r
 * @Return *Response
 * @Return error
 * @Date 2022-02-21 17:16:03
 */
func (u UnimplementedTokenServiceServer) Test(ctx context.Context, r *Request) (*Response, error) {
 // 驗證token
 _, err := u.CheckToken(ctx)
 if err != nil {
  return nil, err
 }
 return &Response{Name: r.GetName()}, nil
}

3.6 服務端代碼

文件: ./cmd/token/server.go

package main

import (
 "52lu/go-rpc/server/tokenservice"
 "fmt"
 "google.golang.org/grpc"
 "net"
)

func main() {
 // 創建grpc服務
 grpcServer := grpc.NewServer()
 // 註冊服務
 tokenservice.RegisterTokenServiceServer(grpcServer, new(tokenservice.UnimplementedTokenServiceServer))
 // 監聽端口
 listen, err := net.Listen("tcp"":1234")
 if err != nil {
  fmt.Println("start error:", err)
  return
 }
 fmt.Println("服務啓動成功....")
 grpcServer.Serve(listen)
}

3.7 實現客戶端代碼

文件: ./cmd/token/client.go

package main

import "C"
import (
 "52lu/go-rpc/server/tokenservice"
 "context"
 "fmt"
 "google.golang.org/grpc"
)

func main() {
 // token信息
 auth := tokenservice.TokenValidateParam{
  Token: "11223",
  Uid:   10000,
 }
 conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth))
 if err != nil {
  fmt.Println("grpc.Dial error ", err)
  return
 }
 defer conn.Close()
 // 實例化客戶端
 client := tokenservice.NewTokenServiceClient(conn)
 // 調用具體方法
 test, err := client.Test(context.TODO()&tokenservice.Request{Name: "張三"})
 fmt.Println("return err:", err)
 fmt.Println("return result:"test)
}

3.8 啓動 & 請求

# 啓動服務
$ go run cmd/token/server.go 
服務啓動成功....

# 發起請求
$ go-rpc go run cmd/token/client.go 
return err: rpc error: code = InvalidArgument desc = token驗證失敗
return result: <nil>
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/0zTvDXfVVhj-l7HBknJONw