必须用mtls+jwt混合方案:mtls确保传输链路加密与客户端证书校验,jwt仅作身份断言,需严格校验iss、aud、exp,隔离用户与服务鉴权逻辑,grpc场景使用perrpccredentials与拦截器,公钥支持热更新与双钥轮换。

必须用 mTLS + JWT 混合,单靠任一机制都不安全。mTLS 保传输链路可信,JWT 做服务身份断言;缺一不可。
mTLS 是服务间通信的硬性前提
没有 mTLS,JWT 就是明文钥匙——中间人能截、能存、能重放。Go 标准库 crypto/tls 完全支持,无需第三方框架。
- 服务端必须设
ClientAuth: tls.RequireAndVerifyClientCert,否则不强制验客户端证书 - 服务端
ClientCAs和客户端RootCAs必须加载同一份 CA PEM,否则握手失败 - 客户端发起请求时,不能用
http.DefaultTransport;必须自定义http.Transport并传入tls.Certificate实例(含client.crt+client.key) - 测试环境可临时设
InsecureSkipVerify: true,但生产环境必须禁用——这是红线
JWT 只负责回答“谁调用了我”,不是加密通道
JWT 不解决传输安全,只做身份断言。它必须由统一授权中心签发,且所有服务只信任一个 iss(如 "https://authz.internal")。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 传递字段必须是
X-Service-Token,绝不能复用Authorization: Bearer——用户 Token 和服务 Token 的签发源、aud、有效期、校验逻辑完全不同 -
aud字段必须严格匹配当前服务名,建议从环境变量读取:os.Getenv("SERVICE_NAME"),硬编码会引发跨服务 token 复用漏洞 - 签名必须用
RS256或ES256,服务端只持公钥;别用HS256,密钥分发和轮换在微服务里就是灾难 - 校验时不能只看
token.Valid;必须手动比对iss、aud、exp,且exp验证要容忍最多 5 秒时钟偏移:token.Claims.VerifyExpiresAt(time.Now().Add(5*time.Second).Unix(), true)
Go 中间件里怎么写校验逻辑
顺序不能错:先验 mTLS,再解 JWT。跳过前者,后者毫无意义。
- HTTP 中间件开头必须加断言:
if c.Request.TLS == nil || len(c.Request.TLS.PeerCertificates) == 0 { c.AbortWithStatus(401); return } - 用
github.com/golang-jwt/jwt/v5,必须调用jwt.ParseWithClaims(tokenStr, &jwt.MapClaims{}, keyFunc),不能用jwt.Parse() - 公钥加载不能每次校验都读文件或远程拉 JWKS;启动时用
io/fs.ReadFile加载 PEM,并缓存为*rsa.PublicKey实例 - gRPC 场景下,用
PerRPCCredentials透传 token,别手动塞metadata;服务端拦截器同样要先验req.TLS,再解析metadata.MD["x-service-token"]
最容易被忽略的是:mTLS 握手成功 ≠ 客户端证书有效 ≠ 该证书对应的服务有调用权限。证书只证明“你是你”,JWT 才说明“你能调什么”。两者叠加,才是服务间鉴权的最小完整闭环。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










