必须用mtls+jwt混合方案:mtls保障传输链路加密与客户端证书校验,jwt仅作身份断言且须严格校验iss、aud、exp;grpc需用perrpccredentials与拦截器,公钥支持热更新与双钥轮换。

服务间调用必须用 mTLS + JWT 混合,单靠签名不安全
只做 HTTP Header 签名(如 X-Signature)或只传 X-Service-Token,在服务间通信里等于裸奔。没有 mTLS,签名和 token 都是明文——中间人能截、能存、能重放。Go 的 crypto/tls 完全支持双向证书校验,这是硬性前提,不是可选项。
常见错误现象:本地测试通,上生产后频繁 401;或者压测时 TLS 握手超时;更隐蔽的是,某台节点时间快了 8 秒,JWT exp 校验直接失败。
- 服务端必须设
ClientAuth: tls.RequireAndVerifyClientCert,否则不强制验客户端证书 - 服务端
ClientCAs和客户端RootCAs必须加载同一份 CA PEM,否则握手失败 - 客户端不能用
http.DefaultTransport;必须自定义http.Transport并传入tls.Certificate(含client.crt+client.key) - 测试环境可临时设
InsecureSkipVerify: true,但生产环境必须禁用——这是红线
JWT 只作身份断言,不是加密通道
JWT 解决的是“谁调用了我”,不是“链路是否可信”。它必须走 mTLS 加密通道传递,且字段校验一个都不能少:必须严格比对 iss、aud、exp,不能只看 token.Valid。
常见错误现象:跨服务复用 token 导致权限越界;时钟偏移导致大量合法请求被拒;用 Authorization: Bearer 复用用户 token,结果服务鉴权逻辑被绕过。
-
iss必须硬编码或从环境变量读取(如os.Getenv("AUTH_ISSUER")),绝不能远程拉取 -
aud必须严格匹配当前服务名,建议从os.Getenv("SERVICE_NAME")读,避免硬编码引发复用漏洞 - 校验
exp时用token.Claims.VerifyExpiresAt(time.Now().Add(5*time.Second).Unix(), true),容忍最多 5 秒时钟偏移 - 头部必须用
X-Service-Token,绝不能复用Authorization—— 用户 token 和服务 token 的签发源、有效期、scope 完全不同
gRPC 场景下必须用 PerRPCCredentials + 拦截器
HTTP 中间件管不了 gRPC 调用,手写校验容易漏。gRPC 的标准方式是客户端通过 PerRPCCredentials 注入 token,服务端用 UnaryInterceptor 统一拦截并校验,且顺序不能错:先验 req.TLS,再解析 metadata.MD["x-service-token"]。
常见错误现象:拦截器返回空 token;gRPC 调用始终 401;日志里显示 md.Get("X-Service-Token") 为空。
- 客户端实现
GetRequestMetadata方法,从 context 提取X-Service-Token,注入metadata.MD{"x-service-token": token} - 服务端拦截器用
metadata.FromIncomingContext(ctx)提取(不是FromOutgoingContext) -
metadata.MD的 key 全是小写,md.Get("X-Service-Token")会返回空,必须用md.Get("x-service-token") - 拦截器开头必须加断言:
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0,不满足直接返回status.Error(codes.Unauthenticated, "mTLS required")
公钥热更新与双钥轮换不能偷懒
线上服务不可能停机更新密钥。硬编码、固定路径读取、单钥长期服役,都是生产事故高发点。公钥必须启动时加载并缓存为 *rsa.PublicKey 实例,不能每次校验都读文件或远程拉 JWKS。
最容易被忽略的是:公钥变更后,旧 token 还在有效期内,新旧公钥必须共存一段时间,否则正在传输中的合法请求会失败。
- 启动时用
io/fs.ReadFile加载 PEM,解析为*rsa.PublicKey并缓存 - 支持双钥轮换:配置中声明主公钥和备公钥路径,校验时先试主钥,失败再试备钥
- 公钥更新应监听文件变化(如 fsnotify)或通过信号触发 reload,避免重启服务
- 别用
HS256—— 密钥分发和轮换在微服务里就是灾难;必须用RS256或ES256,服务端只持公钥
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











