4. 成功响应结构:成功刷新后返回的 access_token, refresh_token, expires_in, token_type 字段详解

当客户端使用有效的刷新令牌(Refresh Token)向令牌端点发起刷新请求时,授权服务器在验证通过后会返回一个标准的成功响应。该响应体通常采用 JSON 格式,包含四个核心字段:access_token、refresh_token、expires_in 和 token_type。理解每个字段的语义、约束和最佳实践,是正确实现刷新流程和进行故障定位的基础。

核心原则:刷新令牌流程的本质是“用旧凭证换取新凭证”。服务器在返回新令牌的同时,可以选择返回一个新的刷新令牌(轮换策略)或保持原有刷新令牌不变(非轮换策略)。响应结构中的 refresh_token 字段行为取决于服务器的策略配置。

4.1 标准成功响应示例

以下是一个典型的成功刷新响应体(JSON 格式):

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}

4.2 字段详解与约束

字段名 类型 必需 说明与约束
access_token 字符串 是 授权服务器颁发的新访问令牌。客户端使用此令牌访问受保护资源。通常为 JWT 或不透明令牌格式。
token_type 字符串 是 令牌类型,固定为 Bearer(RFC 6750)。客户端在后续请求中需将令牌放入 Authorization: Bearer <token> 头。
expires_in 整数 是 访问令牌的有效期,单位为秒。客户端应据此决定何时需要再次刷新。推荐值:3600(1小时)至 86400(24小时)。
refresh_token 字符串 条件必需 新的刷新令牌。当服务器采用轮换策略时必返回;采用非轮换策略时可能不返回或返回原值。客户端应始终使用最新收到的刷新令牌。
调试提示:在故障定位时,首先检查 expires_in 值是否合理。如果服务器返回的 expires_in 为 0 或负数,说明令牌端点配置异常或响应被篡改。同时,token_type 必须严格等于 Bearer(大小写敏感),任何变体(如 bearer、BearerToken)都应视为错误。

4.3 字段间的逻辑关系

四个字段并非孤立存在,它们共同构成了客户端后续行为的依据。以下关系图展示了字段之间的依赖与约束:

成功响应字段逻辑关系 成功响应体 access_token token_type expires_in refresh_token 字段约束与依赖: • access_token 和 token_type 必须同时存在,且 token_type 固定为 "Bearer" • expires_in 决定 access_token 的生命周期,客户端应提前刷新(建议在过期前 10% 时间) • refresh_token 在轮换策略下必返回,客户端必须丢弃旧令牌并使用新令牌

4.4 字段行为对比:轮换策略 vs 非轮换策略

刷新令牌的返回行为直接决定了客户端的安全模型。下表对比了两种常见策略下的字段差异:

对比维度 轮换策略(Rotation) 非轮换策略(Non-Rotation)
refresh_token 是否返回 每次刷新都返回新值 可能不返回,或返回与请求时相同的值
旧令牌有效性 旧刷新令牌立即失效 旧刷新令牌持续有效,直到过期
安全风险 低(令牌泄露窗口短) 高(令牌可被长期重用)
客户端实现复杂度 中等(需管理令牌版本) 低(只需存储一个令牌)
推荐场景 高安全要求的生产环境 低风险内部系统或原型开发
故障定位要点:如果客户端在刷新后未收到新的 refresh_token,但服务器实际采用了轮换策略,客户端将丢失后续刷新能力。此时应检查服务器响应中是否包含 refresh_token 字段,以及客户端是否正确存储了该字段。常见错误是客户端只解析 access_token 而忽略了 refresh_token。

4.5 字段解析与验证清单

在客户端实现中,建议对成功响应执行以下验证步骤:

  1. 验证 JSON 结构完整性:确保响应体是合法 JSON,且包含 access_token、token_type、expires_in 三个必需字段。
  2. 验证 token_type 值:严格检查是否为 Bearer(区分大小写)。
  3. 验证 expires_in 合理性:检查值是否为正整数,且大于 0。建议设置最小阈值(如 60 秒),防止服务器返回异常短的有效期。
  4. 处理 refresh_token:如果响应中包含此字段,立即替换本地存储的旧刷新令牌;如果不包含,保留现有刷新令牌。
  5. 记录时间戳:记录收到响应时的本地时间戳,结合 expires_in 计算令牌的实际过期时间点,用于后续的预刷新调度。
// 客户端验证伪代码示例
function handleRefreshResponse(response) {
  if (!response.access_token || !response.token_type || !response.expires_in) {
    throw new Error('Invalid refresh response: missing required fields');
  }
  if (response.token_type !== 'Bearer') {
    throw new Error('Invalid token_type: expected Bearer');
  }
  if (response.expires_in <= 60) {
    console.warn('Warning: expires_in is too short:', response.expires_in);
  }
  // 更新存储
  storage.set('access_token', response.access_token);
  storage.set('expires_at', Date.now() + response.expires_in * 1000);
  if (response.refresh_token) {
    storage.set('refresh_token', response.refresh_token);
  }
}

4.6 常见响应异常与排查方向

异常现象 可能原因 排查方向
缺少 access_token 服务器内部错误或响应格式错误 检查 HTTP 状态码是否为 200;检查响应 Content-Type 是否为 application/json
token_type 为 bearer(小写) 服务器实现未严格遵循 RFC 6749 联系服务提供商修正;客户端可做大小写容错处理
expires_in 为 0 或负数 服务器时钟异常或配置错误 检查服务器时间同步;检查令牌端点配置
未收到 refresh_token 但后续刷新失败 服务器采用轮换策略但客户端未更新 确认服务器策略;检查客户端是否忽略了响应中的 refresh_token 字段
最佳实践:在开发阶段,建议在客户端日志中完整记录每次刷新响应的原始 JSON 体(注意脱敏处理)。这有助于在故障发生时快速比对字段是否存在、值是否异常,是定位“响应结构问题”最直接的手段。