3. JWT过期机制:exp(过期时间)声明、iat(签发时间)声明、nbf(不早于)声明
JWT 的时间相关声明(Claims)是令牌生命周期管理的核心。后端服务通过验证这些声明来判断令牌是否有效、是否在允许的时间窗口内使用。本章详细解析 exp、iat、nbf 三个标准声明的语义、校验逻辑及常见适配陷阱。
3.1 声明定义与语义对比
以下表格对比了三个时间声明的核心差异:
| 声明 | 全称 | 语义 | 校验方向 | 是否必须 |
|---|---|---|---|---|
| exp | Expiration Time | 令牌的绝对过期时间点 | 当前时间 > exp → 拒绝 | 推荐(通常强制) |
| iat | Issued At | 令牌的签发时间点 | 通常仅记录,不强制校验 | 可选 |
| nbf | Not Before | 令牌生效的最早时间点 | 当前时间 < nbf → 拒绝 | 可选 |
实践要点: 在微服务架构中,
exp 是后端安全合规的必检项;nbf 常用于延迟生效场景(如预激活令牌);iat 主要用于审计和令牌年龄计算。
3.2 exp(过期时间)声明
exp 是 JWT 中最关键的安全声明。它定义了一个 UTC 时间戳(NumericDate),表示令牌在此时间点之后不再被接受。
- 格式要求: 必须为 NumericDate 类型(Unix 时间戳,精确到秒)。
- 校验规则: 服务端在验证签名后,必须检查当前时间是否大于
exp值。若大于,则返回401 Unauthorized并携带错误码TOKEN_EXPIRED。 - 时钟偏差(Clock Skew)处理: 分布式系统中,服务端与客户端可能存在时间差。建议允许 30~60 秒的宽容窗口,但不可过大,否则会削弱安全性。
// 示例:生成包含 exp 的 JWT(Node.js jsonwebtoken 库)
const jwt = require('jsonwebtoken');
const payload = {
sub: 'user_12345',
role: 'admin',
exp: Math.floor(Date.now() / 1000) + 3600 // 1小时后过期
};
const token = jwt.sign(payload, 'your_secret_key');
3.3 iat(签发时间)声明
iat 记录了令牌的签发时间。它本身不用于拒绝令牌,但常与 exp 配合计算令牌的有效时长,或在审计日志中追踪签发来源。
- 典型用途: 判断令牌是否在允许的最大生命周期内(例如:即使
exp未到,但签发时间超过 24 小时,可触发强制刷新)。 - 安全提示: 不要仅依赖
iat做过期判断,必须与exp配合使用。
常见错误: 部分开发者在生成令牌时忘记设置
exp,仅设置 iat。这会导致令牌永不过期,形成严重安全漏洞。后端校验逻辑必须强制要求 exp 存在。
3.4 nbf(不早于)声明
nbf 定义了令牌的生效时间。如果当前时间早于 nbf 值,服务端应拒绝该令牌。此声明常用于以下场景:
- 预签发令牌: 系统提前生成令牌,但指定在未来某个时间点才允许使用。
- 定时任务授权: 仅在特定时间段内允许访问的 API。
// 示例:生成包含 nbf 的 JWT(延迟 5 分钟生效)
const payload = {
sub: 'service_account',
nbf: Math.floor(Date.now() / 1000) + 300, // 5分钟后生效
exp: Math.floor(Date.now() / 1000) + 7200 // 2小时后过期
};
const token = jwt.sign(payload, 'your_secret_key');
3.5 时间声明校验流程图
下图展示了后端服务对 JWT 时间声明的完整校验流程:
3.6 后端安全合规错误码断言
在微服务网关或 API 网关层,针对时间声明校验失败应返回统一的错误码,便于客户端做断言处理:
| 错误场景 | HTTP 状态码 | 错误码 | 错误消息 |
|---|---|---|---|
| 令牌已过期(exp 校验失败) | 401 | TOKEN_EXPIRED |
Token has expired, please refresh |
| 令牌尚未生效(nbf 校验失败) | 401 | TOKEN_NOT_YET_VALID |
Token is not yet valid |
| 令牌签发时间过长(iat 年龄超限) | 401 | TOKEN_TOO_OLD |
Token issued too long ago |
| 缺少 exp 声明 | 400 | MISSING_EXP_CLAIM |
Missing expiration claim |
适配移植建议: 在将 JWT 校验逻辑从单体应用移植到微服务网关时,务必保持时间声明校验的一致性。建议将校验逻辑封装为独立中间件,并统一错误码格式,避免不同服务返回不同的错误结构。
3.7 时间声明关系示意图
下图展示了三个时间声明在令牌生命周期中的位置关系:
3.8 常见适配陷阱与解决方案
- 陷阱1:时区不一致 — 所有时间声明必须使用 UTC 时间戳,避免使用本地时间字符串。
- 陷阱2:忽略 nbf 校验 — 部分后端实现只校验 exp,忽略 nbf,导致预签发令牌提前被使用。
- 陷阱3:时钟偏差设置过大 — 超过 120 秒的偏差窗口会显著降低安全性,建议不超过 60 秒。
- 陷阱4:iat 用于过期判断 — 不要用 iat + 固定时长 替代 exp,因为客户端和服务端的时间计算可能不一致。