不能复用authorization头传服务令牌,因用户token与服务token在签发源、有效期、scope及校验逻辑上完全不同,混用易致权限绕过或误拒;应使用x-service-token字段传递,并配合mtls与严格jwt校验。

服务间调用不能复用面向用户的 Authorization: Bearer 头,必须用专用字段传递服务身份令牌。
为什么不能用 Authorization 传 service-to-service JWT?
用户 Token 和服务 Token 的签发源、有效期、scope、校验逻辑完全不同。混用会导致中间件误判、权限绕过或拒绝合法服务请求。
- 用户 Token 通常由 OAuth2 授权中心签发,
aud是前端应用 ID;服务 Token 的aud必须是目标服务名(如"user-service") - 用户 Token 可能带
refresh_token流程;服务 Token 一律短时效(1h)、无刷新机制,靠服务重启或轮换重签 - 网关或 API 层可能对
Authorization做统一限流/审计,把服务调用也卷进去会干扰指标
X-Service-Token 是最稳妥的传递方式
这是业内微服务间鉴权的事实标准字段,明确区分流量类型,便于日志打标、链路过滤和 WAF 规则隔离。
- 调用方在 HTTP Header 中设置:
c.Request.Header.Set("X-Service-Token", signedToken) - 被调用方中间件只认这个头:
tokenStr := c.GetHeader("X-Service-Token"),不解析Authorization - gRPC 场景下用
PerRPCCredentials实现透传,避免手动塞 metadata - 务必配合 mTLS:校验前先断言
req.TLS != nil && len(req.TLS.PeerCertificates) > 0,否则直接401
JWT 解析时最容易漏掉的三个校验点
仅检查 token.Valid 不够——github.com/golang-jwt/jwt/v5 的默认行为不会校验 iss、aud、exp 是否符合业务语义。
立即学习“go语言免费学习笔记(深入)”;
-
iss必须硬编码比对,如claims["iss"] == "https://authz.internal",不能只信签名 -
aud必须动态匹配当前服务名,建议从环境变量读取:os.Getenv("SERVICE_NAME") -
exp校验后要显式调用token.Claims.VerifyExpiresAt(time.Now().Unix(), true),否则可能跳过 - 别用
jwt.Parse(),必须用jwt.ParseWithClaims(tokenStr, &jwt.MapClaims{}, keyFunc)并手动 cast claims
服务启动时加载的公钥别写死在代码里,也别每次校验都远程拉取 JWKS;用 io/fs.ReadFile 读取 PEM 文件并缓存 *rsa.PublicKey 实例——这是性能和安全的交界点,也是多数人上线后才踩到的坑。










