问题出现得很突然,日志倒是留得挺全。用服务账号私钥签 JWT 调 OAuth 换 access_token,一直返回 400 invalid_grant,原文 Invalid JWT: iat is in the future,本机时间比标准时间快 5 分钟,同一份私钥在另一台机器上正常。
刚开始盯着「私钥与上传的公钥指纹不匹配」,折腾了半个多小时,排掉了。
原因不止一个,得分开看。本机时钟漂移超过服务端五秒容忍窗口。也可能是iat 与 exp 跨度超过一小时被拒。
还有一种情况,aud 填成了带路径的地址。
第一件事是把本机时间和标准时间对齐
w32tm /resync; python -c "import time;print(int(time.time()))"
随后按规范重新签发 JWT 并打印各字段
python sign_jwt.py --iss sa@proj.iam --aud https://oauth.example.com/token --ttl 600 --print
接着用 curl 换一次 token 看返回码
curl -s -X POST https://oauth.example.com/token -d 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion='$JWT -o resp.json -w '%{http_code}\n'
解析返回里的过期时间做好提前刷新
python -c "import json,time;d=json.load(open('resp.json'));print(d['expires_in'])"
这类问题以前遇到过,但这次的触发条件和上次不一样。
改完先别撤,确认这几项。换 token 返回 200,响应里有 access_token 和 expires_in,同一份私钥在两台机器上都能换成功。
JWT 报 invalid_grant 时先看 iat 而不是先换私钥,容器里没装时间同步的镜像时钟漂移很常见。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。
