grpc中http header需手动转为metadata.md并注入context,不可依赖context.withvalue传鉴权信息;服务端须用metadata.fromincomingcontext安全校验token,下游调用需显式透传metadata。

Go gRPC 里怎么把 HTTP Header 转成 context.Context 的 metadata
Header 不会自动进 context,必须手动从 http.Request 或 grpc.ServerStream 中提取并注入。常见错误是直接用 r.Header.Get("Authorization") 拿值,却忘了 gRPC over HTTP/2 实际走的是 metadata.MD,不是原始 Header。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在 gRPC gateway 或中间件中,用
metadata.FromIncomingContext()读取已解析的 metadata(前提是 client 真正发了) - 若用标准 gRPC client,确保调用时传了
metadata.Pairs("authorization", "Bearer xxx"),而不是往http.Header里塞 - HTTP/1.1 + gRPC-Gateway 场景下,需配置
runtime.WithMetadata()回调,把r.Header显式转成metadata.MD - 注意大小写:gRPC metadata key 默认小写,
"Authorization"会被转成"authorization",服务端取时得用小写键
context.WithValue 传鉴权信息为什么在 gRPC 里不推荐
因为 gRPC 的 context 是跨网络序列化的,context.WithValue 写进去的任意 Go 值(比如 *User 结构体)不会被传输,只存在于当前进程内存。下游服务拿到的是空 context,不是你“以为传过去”的那个。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 鉴权字段必须走
metadata.MD,它是 gRPC 协议层定义的、可透传的键值对容器 - 不要在 middleware 里做
ctx = context.WithValue(ctx, userKey, u)然后期望 handler 能拿到;改用ctx = context.WithValue(ctx, userKey, u)仅限本进程内短链路使用(如日志打标),且要配value类型断言 - 如果真需要带结构化数据,应把关键字段(如
user_id、role)拆成 metadata 键值对传,服务端再组装
gRPC server 端如何安全地从 context 提取并校验 token
直接用 metadata.FromIncomingContext(ctx) 取不到就 panic,或取到空字符串就放行,是线上高频事故点。metadata 可能缺失、格式错、签名过期,每一步都得防御。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先检查
ok:用md, ok := metadata.FromIncomingContext(ctx),!ok表示根本没收到 metadata,该拒绝不犹豫 - 再查 key:用
md["authorization"](小写)取值,注意它返回[]string,取[0]前先判空 - token 解析别裸写
strings.Split():用strings.Cut()或现成库(如github.com/golang-jwt/jwt/v5)校验 signature 和 exp - 避免在每个 handler 里重复写校验逻辑——封装成
AuthInterceptor,用grpc.UnaryInterceptor统一拦截
跨服务调用时 metadata 丢失的典型场景和修复点
下游服务收不到上游传的 authorization,90% 是因为调用方没显式把 metadata 透传下去。gRPC 不会自动继承父 context 的 metadata 到子请求。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 调下游前,必须手动构造:
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", md["authorization"][0]) - 如果用了
WithTimeout或WithCancel新建 ctx,记得在新 ctx 上再 append metadata,原 ctx 的 metadata 不会自动迁移 - 注意 interceptor 链顺序:auth interceptor 必须在 tracing interceptor 之后执行,否则 tracing 可能覆盖掉 auth metadata
- 本地调试时可用
grpcurl -H "authorization: Bearer xxx"测试是否真能收到,绕过 client 代码干扰
metadata 本质是 string→[]string 映射,没有类型、没有嵌套、不校验格式。所有业务含义(比如 “这个 authorization 是 JWT 还是 API Key”)全靠约定和代码守卫——漏一处,整条链就裸奔。










