通过拦截器和metadata实现grpc的auth认证#Golang

通过拦截器和 Metadata 实现 gRPC 的 Auth 认证

在 gRPC 服务中实现基于拦截器和 Metadata 的认证。我们将使用一个简单的 Greeter 服务来演示该过程。

  1. 创建 Protocol Buffers 文件
  2. 实现 gRPC 服务端
  3. 实现自定义认证拦截器
  4. 实现 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 中提取 appidappkey,并验证它们是否匹配预期的值。如果认证失败,拦截器将返回一个未认证错误;如果认证成功,拦截器将继续处理请求。

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:返回请求的元数据(包括 appidappkey)。
  • RequireTransportSecurity:指示是否需要传输层安全性。

我们使用 grpc.WithPerRPCCredentials(customCredential{}) 创建 gRPC 选项,并在连接服务器时应用这些选项。

注:grpc.WithInsecure()已经不再使用该使用为 grpc.WithTransportCredentials(insecure.NewCredentials() 博客地址

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
gRPC拦截器实现gRPC请求的内部转发,可以使用gRPC提供的Stub对象来发送gRPC请求。首先,需要在代码中注入Stub对象,代码如下: ```java @Autowired private XxxServiceGrpc.XxxServiceBlockingStub xxxServiceBlockingStub; ``` 然后,在拦截器中使用Stub对象来发送gRPC请求,代码如下: ```java public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<RespT, ReqT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { // 获取请求的方法名称和参数 String methodName = call.getMethodDescriptor().getFullMethodName(); ReqT request = call.getMessage(); // 构造请求的上下文 Context.CancellableContext withCancellation = Context.current().withCancellation(); withCancellation.attach(); // 发送gRPC请求并获取响应结果 RespT response = xxxServiceBlockingStub.withDeadlineAfter(5, TimeUnit.SECONDS).withInterceptors(new HeaderClientInterceptor(headers)).execute(request); // 返回gRPC响应结果给调用方 return new ServerCall.Listener<ReqT>() {}; } ``` 在这段代码中,我们使用Stub对象向目标gRPC服务发送gRPC请求,并将请求头和参数传递过去。然后,我们将目标gRPC服务的响应结果返回给调用方。 需要注意的是,在发送gRPC请求时,我们可以使用`withDeadlineAfter()`方法设置请求的超时时间,以避免请求一直阻塞。另外,我们还可以使用`withInterceptors()`方法设置请求的拦截器,以添加自定义的请求头等信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

席万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值