介绍
gRPC
为每个 gRPC
方法调用提供了 token
认证支持,可以基于用户传入的 token
判断用户是否登录、以及权限…,实现 token
认证的前提是,需要定义一个结构体,并实现 grpc.PerRPCCredentials
接口。
grpc.PerRPCCredentials
1 2 3 4 5 6
| type PerRPCCredentials interface { GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) RequireTransportSecurity() bool }
|
步骤梳理
- 步骤一: 编写
proto
文件。
- 步骤二: 生成
go
代码。
- 步骤三: 实现
grpc.PerRPCCredentials
接口。
- 步骤四: 编写验证
token
方法。
- 步骤五: 实现被调用的方法
Test
。
- 步骤六: 编写服务端代码。
- 步骤七: 编写客户端代码。
- 步骤八: 启动服务 & 发起请求。
代码实现
编写 proto
文件
文件: go-advanced/grpc/3/proto/token.proto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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); }
|
生成 Go
代码
1
| ➜ 3 protoc --go_out=. --go-grpc_out=. proto/token.proto
|
实现 grpc.PerRPCCredentials
文件:go-advanced/grpc/3/server/tokenservice/token_grpc.pb.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| ...
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 } ...
|
编写验证 token
方法
文件:go-advanced/grpc/3/server/tokenservice/token.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
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] uidInfo, ok := md["uid"] if !ok { return nil, status.Error(codes.InvalidArgument, "uid不存在") } uid = uidInfo[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 }
|
实现被调用的方法 Test
文件:go-advanced/grpc/3/server/tokenservice/token_grpc.pb.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| ...
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 } ...
|
服务端代码
文件:go-advanced/grpc/3/server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
package main
import ( "fmt" "go-advanced/grpc/3/server/tokenservice" "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) }
|
实现客户端代码
文件:go-advanced/grpc/3/client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
package main
import ( "context" "fmt" "go-advanced/grpc/3/server/tokenservice" "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) }
|
启动 & 请求
1 2 3 4 5 6 7 8
| ➜ 3 go run server.go 服务启动成功....
➜ 3 go run client.go return err: rpc error: code = InvalidArgument desc = token验证失败 return result: <nil>
|