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) RequireTransportSecurity() bool }
|
2.步骤梳理
- 步骤一: 编写
proto
文件。
- 步骤二: 生成
go
代码。
- 步骤三: 实现
grpc.PerRPCCredentials
接口。
- 步骤四: 编写验证
token
方法。
- 步骤五: 实现被调用的方法
Test
。
- 步骤六: 编写服务端代码
- 步骤七: 编写客户端代码
- 步骤八: 启动服务 & 发起请求
3. 代码实现
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
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 }
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" )
type TokenAuth struct{}
func (t TokenAuth) CheckToken(ctx context.Context) (*Response, error) { md, b := metadata.FromIncomingContext(ctx) if !b { return nil, status.Error(codes.InvalidArgument, "token信息不存在") } var token, uid string tokenInfo, ok := md["token"] if !ok { return nil, status.Error(codes.InvalidArgument, "token不存在") } token = tokenInfo[0] 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
type UnimplementedTokenServiceServer struct { TokenAuth }
func (u UnimplementedTokenServiceServer) Test(ctx context.Context, r *Request) (*Response, error) { _, 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() { 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() { 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>
|