golang微服务鉴权需分层控制:先验证jwt签名与时效(用jwt.parsewithclaims配密钥回调、检查alg白名单、校验token.valid),再基于perms字段做无状态权限决策;内部服务须签发短时效、限定aud/iss的专用token,禁用原始token透传与实时db查权。

jwt.ParseWithClaims 必须传密钥回调,不能省略第三个参数
常见错误是写成 jwt.Parse(tokenStr, &MyClaims{}),这会导致解析时无法获取密钥,直接 panic 或静默失败。v5 版本强制要求提供密钥函数,否则无法完成签名验证。
- 必须用
jwt.ParseWithClaims(tokenStr, &MyClaims{}, func(*jwt.Token) (interface{}, error) { return []byte(secret), nil }) - 密钥长度要够:HS256 至少 32 字节,别用
"my-secret"这种短字符串,推荐用crypto/rand.Read生成 - 回调函数里要检查
token.Method.Alg()是否在白名单内(如jwt.SigningMethodHS256.Name),防止 alg=none 攻击 - 解析后必须同时判断
err == nil和token.Valid,二者缺一不可
微服务间传递 Token 不能复用前端原始 JWT
原始 Token 可能含 email、phone 等敏感字段,过期时间长(如 24h),且签发密钥与内部服务不一致——直接透传等于把用户凭证暴露给所有下游服务。
- 网关或认证服务校验原始 Token 后,应签发新 Token:
sub设为用户 ID,aud明确设为下游服务名(如"payment-service"),exp缩短至 5 分钟 - 内部 Token 的
iss应固定为网关服务名(如"api-gateway"),下游服务校验时强制比对token.Claims.(jwt.MapClaims)["iss"] == "api-gateway" - HTTP 透传用
X-Service-TokenHeader(而非Authorization),避免和前端请求混淆;gRPC 场景用metadata.Pairs("x-service-token", innerToken) - 绝对不要把原始 Token 存进
context.WithValue向下传递——panic 日志或 debug 接口可能意外输出它
权限控制必须和身份验证分离,别在鉴权中间件里查数据库
JWT 本身应携带足够授权依据,比如 role: "admin" 或 perms: ["user:read", "order:write"]。如果每次请求都去 DB 查角色表,就失去了 JWT 无状态的优势,还引入单点瓶颈。
- 登录时生成 Token 就该把最小必要权限写进
perms字段,而不是只放role再靠中间件查映射表 - 权限校验逻辑应独立于 JWT 解析中间件:先过身份中间件(注入
userID和perms到 context),再挂载权限中间件(如RequirePerm("order:write")) - 若需动态权限(如基于资源的 ACL),也应预加载到内存或用本地缓存(如
map[string][]string),避免每次请求触发 RPC 或 DB 查询 - RBAC 场景下,
role字段只用于快速分流,具体接口级控制仍靠perms数组匹配
错误处理要区分原因但不泄露细节,日志必须带 traceID
返回 401 是底线,但生产环境不能只返回 {"error": "invalid token"}——攻击者会利用响应差异做探测;也不能返回 {"error": "signature invalid: key mismatch"} 这种密钥相关信息。
- 统一返回泛化错误码:
401 Unauthorized对所有失败,body 仅提示"authentication failed"(开发环境可加debug: true开关输出详细 err) - 每种失败类型打不同级别日志:
WARN记录token expired、signature verification failed、missing jti claim;ERROR记录密钥加载失败等系统问题 - 日志必须包含请求的
traceID(从X-Trace-IDHeader 提取或生成),方便关联上下游调用链 - 高频失败(如 1 分钟内同一 IP 10 次无效 Token)建议记录并触发告警,但不要在中间件里直接限流——那是网关层职责
iat 和服务器时钟偏移。即使设置了 WithExpiresAt,若服务间 NTP 同步偏差超过 1 秒,VerifyExpiresAt 就可能误判。上线前必须确认所有节点时钟误差 jwt.WithLeeway(1*time.Second)。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











