gRPC UnaryInterceptor 必须严格匹配 grpc.UnaryServerInterceptor 签名,正确调用 handler(ctx, req) 并返回 (interface{}, error),从 metadata 小写键取 Bearer Token 解析 JWT,RBAC 权限校验需在拦截器中基于 info.FullMethod 精确匹配预热策略,客户端须用 metadata.AppendToOutgoingContext 注入 Token。

gRPC UnaryInterceptor 怎么写才不 panic 且生效
拦截器函数签名必须严格匹配 grpc.UnaryServerInterceptor 类型,否则运行时 handler 可能为 nil,调用时直接 panic。常见错误是漏掉 handler(ctx, req) —— 这不是可选步骤,而是业务逻辑执行的唯一入口。
- 函数返回值类型必须是
(interface{}, error),不能省略 resp 或 err - 鉴权失败时,应返回
status.Error(codes.Unauthenticated, "..."),而非panic或裸return nil, errors.New(...) - metadata 键名必须小写:
md.Get("authorization")✅,md.Get("Authorization")❌(HTTP/2 自动转小写) - 不要在
defer中读取resp或err—— 它们是handler()的返回值,defer执行时还未赋值
从 context 提取 JWT Token 的正确姿势
Token 通常放在 authorization metadata header 中,格式为 Bearer <token></token>。直接用 strings.Split 解析比正则更轻量、更可控。
- 先调
metadata.FromIncomingContext(ctx)获取metadata.MD - 再用
md.Get("authorization")拿值(注意:返回的是[]string,取[0]) - 手动切分:
strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer ")) - JWT 解析推荐用
github.com/golang-jwt/jwt/v5,验证exp、aud、iss等关键 claim - 校验失败必须早 return,避免后续 handler 被调用
权限检查该放在拦截器里还是业务层
RBAC 判断必须下沉到拦截器,而不是每个 SayHello 方法里手写 if !hasPerm(ctx, "user:read")。否则权限逻辑分散、易遗漏、难审计。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 拦截器里解析出
service_id、role、permissions后,查缓存(如 Redis)或本地 map 做白名单匹配 - 匹配依据是
info.FullMethod(如/pb.UserService/GetUser),不是路径字符串 - 避免在拦截器里查 DB —— 高频调用下会拖慢整个链路;提前加载或异步刷新权限快照
- 若权限模型复杂(如 ABAC),建议用
casbin的Enforce接口,但确保其model和policy已预热
客户端怎么传 Token 才能让服务端拦截器拿到
服务端拦截器收不到 Token,90% 是客户端没正确注入 metadata。gRPC 不自动携带 HTTP header,必须显式附加。
立即学习“go语言免费学习笔记(深入)”;
- 用
metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token) - 不是
context.WithValue,也不是往 http.Header 写 —— gRPC 的 metadata 是独立传输层机制 - 如果用了网关(如 Envoy),确认它没 strip 掉
authorizationheader;某些配置默认过滤敏感头 - 调试时可在拦截器开头加日志:
log.Printf("got headers: %+v", md),验证 key 是否存在、值是否为空
info.FullMethod 和权限规则之间的映射关系 —— 它不是 REST 路径,不能靠字符串前缀匹配“/user/*”,而要精确对应 proto service/method 名。一旦配错,拦截器看似运行,实则放行所有请求。

















