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 时间声明的完整校验流程:

JWT 时间声明校验流程 收到 JWT 令牌 校验 nbf 声明 当前时间 < nbf ? 拒绝:TOKEN_NOT_YET_VALID 通过 校验 exp 声明 当前时间 > exp ? 拒绝:TOKEN_EXPIRED 通过 校验 iat(可选) 令牌年龄 > 最大允许时长 ? 拒绝:TOKEN_TOO_OLD 通过 令牌有效,放行 说明: 校验顺序为 nbf → exp → iat。任何一步失败均立即拒绝,不继续后续校验。 时钟偏差建议:exp 和 nbf 校验时可容忍 ±30 秒偏差。

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 时间声明关系示意图

下图展示了三个时间声明在令牌生命周期中的位置关系:

JWT 时间声明生命周期关系 t₀ iat nbf exp 未签发(不存在) 等待生效 有效期内 过期 关键关系: • iat ≤ nbf ≤ exp(逻辑约束,非强制) • 若 nbf 未设置,则默认令牌签发后立即生效 • 令牌有效窗口 = exp - nbf(若 nbf 未设置则 = exp - iat)

3.8 常见适配陷阱与解决方案

  • 陷阱1:时区不一致 — 所有时间声明必须使用 UTC 时间戳,避免使用本地时间字符串。
  • 陷阱2:忽略 nbf 校验 — 部分后端实现只校验 exp,忽略 nbf,导致预签发令牌提前被使用。
  • 陷阱3:时钟偏差设置过大 — 超过 120 秒的偏差窗口会显著降低安全性,建议不超过 60 秒。
  • 陷阱4:iat 用于过期判断 — 不要用 iat + 固定时长 替代 exp,因为客户端和服务端的时间计算可能不一致。