通过拦截器和 Metadata 实现 gRPC 的 Auth 认证
文章目录
- 通过拦截器和 Metadata 实现 gRPC 的 Auth 认证
- 1. 创建 Protocol Buffers 文件
- 2. 实现 gRPC 服务端
- 3. 实现自定义认证拦截器
- 4. 实现 gRPC 客户端
在 gRPC 服务中实现基于拦截器和 Metadata 的认证。我们将使用一个简单的 Greeter
服务来演示该过程。
- 创建 Protocol Buffers 文件
- 实现 gRPC 服务端
- 实现自定义认证拦截器
- 实现 gRPC 客户端
1. 创建 Protocol Buffers 文件
首先,我们定义了一个简单的 Greeter
服务,它包含一个 SayHello
方法。该方法接受 HelloRequest
请求并返回 HelloReply
响应。以下是 greeter.proto
文件的内容:
syntax = "proto3";
option go_package = ".;proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
2. 实现 gRPC 服务端
接下来,我们实现 gRPC 服务端。在服务端中,我们将使用自定义的认证拦截器来验证请求中的 Metadata。以下是服务端的实现代码:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"net"
"OldPackageTest/grpc_token_auth_test/proto"
)
// Server 结构体实现了 proto.UnimplementedGreeterServer 接口
type Server struct {
proto.UnimplementedGreeterServer
}
// SayHello 方法实现了 Greeter 服务的 SayHello RPC 方法
func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply, error) {
return &proto.HelloReply{
Message: "hello " + request.Name,
}, nil
}
func main() {
// 定义拦截器,用于在处理每个请求之前和之后执行自定义逻辑
interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
fmt.Println("接收到了一个新的请求")
// 从上下文中提取元数据
md, ok := metadata.FromIncomingContext(ctx)
fmt.Println(md)
if !ok {
// 如果没有元数据,则返回未认证错误
return resp, status.Error(codes.Unauthenticated, "无token认证信息")
}
// 从元数据中提取 appid 和 appkey
var (
appid string
appkey string
)
if va1, ok := md["appid"]; ok {
appid = va1[0]
}
if va1, ok := md["appkey"]; ok {
appkey = va1[0]
}
// 检查 appid 和 appkey 是否匹配预期值
if appid != "101010" || appkey != "i am a key" {
return resp, status.Error(codes.Unauthenticated, "无token认证信息")
}
// 如果认证通过,调用实际的 RPC 方法处理请求
res, err := handler(ctx, req)
fmt.Println("请求已经完成")
return res, err
}
// 创建一个新的 gRPC 服务器,并应用拦截器
opt := grpc.UnaryInterceptor(interceptor)
g := grpc.NewServer(opt)
// 注册 Greeter 服务
proto.RegisterGreeterServer(g, &Server{})
// 监听 TCP 端口
lis, err := net.Listen("tcp", "0.0.0.0:50051")
if err != nil {
panic("failed to listen:" + err.Error())
}
// 启动 gRPC 服务器
err = g.Serve(lis)
if err != nil {
panic("failed to start grpc:" + err.Error())
}
}
3. 实现自定义认证拦截器
在上面的代码中,我们实现了一个自定义的认证拦截器 interceptor
。该拦截器从请求的 Metadata 中提取 appid
和 appkey
,并验证它们是否匹配预期的值。如果认证失败,拦截器将返回一个未认证错误;如果认证成功,拦截器将继续处理请求。
4. 实现 gRPC 客户端
接下来,我们实现 gRPC 客户端。客户端使用自定义的凭据类 customCredential
在每个请求中添加认证信息。以下是客户端的实现代码:
package main
import (
"OldPackageTest/grpc_token_auth_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
)
// customCredential 结构体用于实现 gRPC 的自定义认证凭据
type customCredential struct{}
// GetRequestMetadata 方法返回请求的元数据(包括认证信息)
func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"appid": "101010",
"appkey": "i am a key",
}, nil
}
// RequireTransportSecurity 方法指示是否需要传输层安全性,这里返回 false 表示不需要
func (c customCredential) RequireTransportSecurity() bool {
return false
}
func main() {
// 创建一个包含自定义凭据的 gRPC 选项
grpc.WithPerRPCCredentials(customCredential{})
// 定义 gRPC 连接选项
var opts []grpc.DialOption
// 使用不安全连接(不加密)
opts = append(opts, grpc.WithInsecure())
// 添加自定义凭据选项
opts = append(opts, grpc.WithPerRPCCredentials(customCredential{}))
// 连接到 gRPC 服务器
conn, err := grpc.Dial("127.0.0.1:50051", opts...)
if err != nil {
panic(err) // 连接失败时,终止程序并输出错误信息
}
defer conn.Close() // 在函数返回前关闭连接
// 创建 Greeter 客户端
c := proto.NewGreeterClient(conn)
// 向服务器发送 SayHello 请求,并等待响应
r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
if err != nil {
panic(err) // 请求失败时,终止程序并输出错误信息
}
// 输出服务器返回的消息
fmt.Println(r.Message)
}
在上面的代码中,我们实现了 customCredential
结构体,它实现了 grpc.PerRPCCredentials
接口。该接口包含两个方法:
-
GetRequestMetadata
:返回请求的元数据(包括appid
和appkey
)。 -
RequireTransportSecurity
:指示是否需要传输层安全性。
我们使用 grpc.WithPerRPCCredentials(customCredential{})
创建 gRPC 选项,并在连接服务器时应用这些选项。
注:grpc.WithInsecure()已经不再使用该使用为 grpc.WithTransportCredentials(insecure.NewCredentials() 博客地址