【gRPC-gateway】auth-通过拦截器从上下文中提取元数据用于认证,与从http header转发待认证数据到上下文进行验证,go案例

从grpc上下文中提取元数据用于认证 案例

interceptor.go

go 复制代码
package server

import (
	"context"
	"errors"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"strings"
)

// UnaryInterceptor 是一个 unary RPC 的拦截器,用于在处理请求前进行身份认证。
// 参数:
//
//	ctx - 上下文,用于传递请求相关的元数据。
//	req - 请求的数据。
//	info - 包含被拦截的 RPC 方法的信息。
//	handler - 下游的处理器,用于执行实际的 RPC 方法。
//
// 返回值:
//
//	响应数据和可能的错误。
func UnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
	// 在执行实际的处理函数前进行身份认证。
	err = auth(ctx)
	if err != nil {
		return nil, err
	}
	// 身份认证通过后,调用实际的处理函数。
	return handler(ctx, req)
}

// StreamInterceptor 是一个 streaming RPC 的拦截器,用于在处理请求前进行身份认证。
// 参数:
//
//	srv - 服务器的实现。
//	ss - 服务器流,用于读取请求和写入响应。
//	info - 包含被拦截的 streaming RPC 方法的信息。
//	handler - 下游的处理器,用于执行实际的 streaming RPC 方法。
//
// 返回值:
//
//	可能的错误。
func StreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
	// 在执行实际的处理函数前进行身份认证。
	err := auth(ss.Context())
	if err != nil {
		return err
	}
	// 身份认证通过后,调用实际的处理函数。
	return handler(srv, ss)
}

// auth 函数用于执行身份认证逻辑。
// 参数:
//
//	ctx - 上下文,用于传递请求相关的元数据。
//
// 返回值:
//
//	如果身份认证失败,返回错误;否则返回 nil。
func auth(ctx context.Context) error {
	// 从上下文中提取元数据,用于身份认证。
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return errors.New("元数据获取失败,身份认证失败")
	}
	// 检查元数据中是否包含认证信息。
	authorization := md["authorization"]
	if len(authorization) < 1 {
		return errors.New("元数据获取失败,身份认证失败")
	}
	// 提取并验证 token。
	token := strings.TrimPrefix(authorization[0], "Bearer ")
	if token != bearerToken {
		return errors.New("身份认证失败")
	}
	// 身份认证成功。
	return nil
}

// bearerToken 是用于身份认证的密钥。
var bearerToken = "asdfgh"

main.go

go 复制代码
s := grpc.NewServer(grpc.UnaryInterceptor(server.UnaryInterceptor), grpc.StreamInterceptor(server.StreamInterceptor))

从http header转发到grpc上下文进行认证 案例

  • 因为gateway负责,将远端http请求转为gRPC从而与grpc服务器通信,所以应该在gateway中处理http header,使其中需要的部分放入grpc上下文中

通过在runtime.NewServeMux(inComingOpt)中添加处理函数inComingOpt可以解决

可以看到NewServeMux还支持很多操作

gateway.go

go 复制代码
package gateway

import (
	"context"
	"flag"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	gw "golang19-grpc-gateway/user/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"net/http"
)

var (
	// grpc服务器端点
	grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:50051", "gRPC server endpoint")
)

// Run 启动一个 HTTP 服务器,用于将 HTTP 请求转发到 gRPC 服务器。
// 该函数配置了一个 HTTP 多路复用器以处理不同的 HTTP 路径,并将这些路径与 gRPC 方法关联起来。
// 它还设置了一个不安全的 gRPC 连接选项,仅适用于开发环境。
// 返回值: 如果 HTTP 服务器启动失败或在处理请求时遇到错误,则返回错误。
func Run() error {
	// 初始化一个上下文对象,用于取消操作和传递请求范围的值。
	ctx := context.Background()
	// 创建一个可取消的上下文,以便在函数退出时取消可能的挂起操作。
	ctx, cancel := context.WithCancel(ctx)
	// 确保在函数退出时取消上下文。
	defer cancel()

	// inComingOpt 配置了一个处理传入的http请求头的选项
	// 该选项使用一个函数来检查和转换请求头的键
	// 该匹配器函数决定哪些传入的HTTP头应该被传递给gRPC上下文,以及是否应该修改这些头的键。
	inComingOpt := runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {
		// 如果matcher返回true,则该标头将传递给gRPC上下文。要在传递给gRPC上下文之前转换标头,匹配器应返回修改后的标头。
		switch s {
		// 对于"Service-Authorization"头,将其转换为"service-authorization"并传递给gRPC上下文。
		case "Service-Authorization":
			return "authorization", true
		// 不修改请求头的键,不传递给上下文
		default:
			return "", false
		}
	})

	// 创建一个 HTTP 处理多路复用器,用于将 HTTP 请求分发到不同的处理程序。
	mux := runtime.NewServeMux(inComingOpt)
	// 为 "/upload" 路径添加一个处理程序,处理 POST 请求。
	mux.HandlePath("POST", "/upload", uploadHandler)

	// 配置一个不安全的 gRPC 连接选项,这仅适用于开发环境。
	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
	// 将 gRPC 方法映射为 HTTP 请求,使 HTTP 客户端可以与 gRPC 服务器通信。
	err := gw.RegisterUserHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	// 如果注册处理程序时发生错误,返回该错误。
	if err != nil {
		return err
	}

	// 启动 HTTP 服务器,监听端口 8081,使用配置好的多路复用器处理请求。
	return http.ListenAndServe(":8081", mux)
}

这样改造后,无论是验证上下文中指定数据,还是验证请求头中的数据,只需要保持提取后,重新指定的待验证的key相同就可同时实现

结果演示

既可以验证authorization


也可以验证headers


https://github.com/0voice

相关推荐
名字还没想好☜23 分钟前
Go 的 select 实战:超时、非阻塞收发与优雅退出的三个套路
开发语言·数据库·golang·go·并发
编程序的员40 分钟前
使用OpenTelemetry来监控GoLang应用
运维·golang·monitoring·apm·opentelemetry
ttwuai14 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
布朗克16818 小时前
Go 入门到精通-31-编码与序列化
开发语言·qt·golang·编码与序列化
ControlRookie21 小时前
第04篇_把 HTTP 放进 PLC:扫描周期、缓冲区、状态机和能力边界
http·codesys·plc通信
microrain21 小时前
从硬编码到热插拔:SagooIoT插件架构如何打破物联网平台的扩展瓶颈
物联网·golang·开源·sagooiot
記億揺晃着的那天1 天前
HTTPS 页面内网直连 NAS:解决 Mixed Content 与公网带宽瓶颈
网络协议·http·https·nas
geovindu1 天前
go:Backtracking Algorithm
开发语言·后端·算法·golang·回溯算法
qq_452396231 天前
第六篇:《并发编程核心:Goroutine 与 Channel》
golang
llwszx1 天前
【Java/Go后端手撸原生Agent(第五篇):多工具并行调用 + BashTool执行引擎 + Judge证据链升级】
java·后端·golang·状态机·pydantic·agnet·llm-as-judge