【注意】最后更新于 September 28, 2022,文中内容可能已过时,请谨慎使用。
介绍
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)
// 是否使用安全链接(TLS)
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
|
...
/**
* GetRequestMetadata
* @Description: 返回token信息
* @Author: Shershon
* @Receiver x
* @Param ctx
* @Param uri
* @Return map[string]string
* @Return error
* @Date 2022-09-23 17:58:55
**/
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
}
/**
* RequireTransportSecurity
* @Description: 自定义认证是否开启TLS
* @Author: Shershon
* @Receiver x
* @Return bool
* @Date 2022-09-23 18:00:02
**/
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
|
/**
* @Author: shershon
* @Description:
* @Date: 2022/09/23 18:03
*/
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
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
|
...
// 匿名TokenAuth,可以直接调用验证token方法
type UnimplementedTokenServiceServer struct {
TokenAuth
}
/**
* Test
* @Description: 实现Test方法(生成的代码,默认不实现具体逻辑)
* @Author: Shershon
* @Receiver u
* @Param ctx
* @Param r
* @Return *Response
* @Return error
* @Date 2022-09-23 18:19:26
**/
func (u UnimplementedTokenServiceServer) Test(ctx context.Context, r *Request) (*Response, error) {
//return nil, status.Errorf(codes.Unimplemented, "method Test not implemented")
// 验证token
_, 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
|
/**
* @Author: shershon
* @Description:
* @Date: 2022/09/23 18:20
*/
package main
import (
"fmt"
"go-advanced/grpc/3/server/tokenservice"
"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)
}
|
实现客户端代码
文件: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
|
/**
* @Author: shershon
* @Description:
* @Date: 2022/09/23 18:23
*/
package main
import (
"context"
"fmt"
"go-advanced/grpc/3/server/tokenservice"
"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)
}
|
启动 & 请求
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>
|