错误信息
token has invalid claims: token is expired
也可能输出为
- Token is expired
- token has invalid claims: token is not valid yet
- token has invalid claims: token used before issued
golang-jwt v4 打印的是“Token is expired”(T 大写)。v5 的其他声明错误使用同样的“token has invalid claims:”前缀。
golang-jwt/jwt (Go) — jwt.ErrTokenInvalidClaims joined with jwt.ErrTokenExpired
含义
令牌的签名验证通过了,但 `exp` 声明的时间已经过去(即使算上你配置的容差)。golang-jwt v5 把它报告为 `ErrTokenInvalidClaims` 与 `ErrTokenExpired` 的组合;同一类错误中,`nbf` 在未来时报“token is not valid yet”,`iat` 在未来时报“token used before issued”。解析出的令牌仍会与错误一起返回,但不得信任它。
原因
令牌超过了有效期
常见访问令牌的有效期通常为 5~60 分钟。缓存令牌、很久之后才重试请求,或者在长时间运行的任务中保存令牌的客户端,会在 `exp` 之后才出示它。
验证方的时钟走快了
常见时钟走快的服务器会让每个令牌都提前过期。容器继承宿主机的时钟,因此一台没有配置 NTP 的宿主机会影响其上的所有服务。
客户端没有刷新机制
偶见保存了访问令牌却从不使用刷新令牌(或者从不处理 401)的单页应用和 CLI,在第一个令牌过期后就会一直失败。
exp 的单位写错了
罕见`exp` 是自纪元(epoch)以来的秒数。如果颁发者把毫秒除了两次,或者使用了本地时间转换,就可能签发出一开始就已过期的令牌。
修复方法
- 1.
识别过期并让客户端刷新
把 `ErrTokenExpired` 映射为带 `invalid_token` 提示的 401。客户端随后使用刷新令牌(或者重新进行身份验证)并重试。
go token, err := jwt.Parse(raw, keyfunc, jwt.WithValidMethods([]string{"ES256"})) if errors.Is(err, jwt.ErrTokenExpired) { w.Header().Set("WWW-Authenticate", `Bearer error="invalid_token", error_description="token expired"`) http.Error(w, "token expired", http.StatusUnauthorized) // client refreshes and retries return } - 2.
允许少量时钟偏差,并要求必须有 exp
几秒的容差可以吸收机器之间的时钟漂移。`WithExpirationRequired` 堵住的是相反方向的漏洞:否则没有 `exp` 的令牌永远不会过期。
go parser := jwt.NewParser( jwt.WithValidMethods([]string{"ES256"}), jwt.WithLeeway(30*time.Second), // absorbs clock skew for exp, nbf and iat jwt.WithExpirationRequired(), // tokens without exp are rejected too )