4. Mock服务设计原则与架构:Mock服务的目标、接口定义、状态管理、延迟模拟、错误注入设计

在构建OAuth2.0 Mock服务时,设计原则直接决定了服务的可用性、可观测性以及测试覆盖能力。一个优秀的Mock服务不仅仅是“返回假数据”,而是一个具备可控状态、可编程延迟、可注入错误的仿真系统。本节将围绕Mock服务的核心设计目标,拆解接口定义、状态管理、延迟模拟与错误注入四大关键架构维度。

4.1 Mock服务设计目标

OAuth2.0 Mock服务的核心目标是在不依赖真实授权服务器的情况下,模拟完整的授权码流程、客户端凭证流程、令牌刷新与撤销流程。设计目标可归纳为以下五点:

目标 说明 可观测性关注点
协议兼容 严格遵循RFC 6749、RFC 7009、RFC 7662标准 请求/响应结构校验日志
状态可控 支持多租户、多会话、多授权码状态独立管理 状态变更事件、过期时间追踪
延迟可编程 支持按端点、按场景配置固定/随机/阶梯延迟 延迟分布直方图、P99/P50指标
错误可注入 支持HTTP错误码、协议错误、网络级异常注入 错误注入触发记录、错误率告警
可观测性原生 内置Metrics、Tracing、Logging三支柱 请求链路追踪、指标暴露、结构化日志
设计原则提示: Mock服务应遵循“契约优先”原则,先定义OpenAPI/Swagger规范,再实现模拟逻辑。这样可确保接口定义与真实授权服务器保持结构一致,降低后续集成测试的偏差风险。

4.2 接口定义与端点设计

Mock服务需要暴露与真实OAuth2.0授权服务器一致的端点。以下为核心端点定义清单:

  • 授权端点 GET /oauth2/authorize — 模拟用户授权页面,支持response_type=code,返回授权码或错误。
  • 令牌端点 POST /oauth2/token — 支持authorization_code、client_credentials、refresh_token三种grant_type。
  • 令牌撤销端点 POST /oauth2/revoke — 模拟令牌撤销逻辑,返回200或400。
  • 令牌 introspection 端点 POST /oauth2/introspect — 返回令牌的活跃状态、scope、client_id等信息。
  • JWKS端点 GET /oauth2/jwks — 返回模拟的JSON Web Key Set,用于令牌签名验证。
  • 管理端点 POST /mock/admin/state — 用于测试框架动态设置Mock状态、延迟、错误注入规则。
架构建议: 管理端点应与OAuth2.0协议端点分离,避免污染协议语义。建议使用独立的端口或路径前缀(如 /mock/admin/),并在生产部署时通过防火墙限制管理端点的访问。

4.3 状态管理设计

状态管理是Mock服务的核心复杂度所在。OAuth2.0流程涉及授权码、访问令牌、刷新令牌、客户端注册等多类状态对象。设计时需考虑以下维度:

状态对象 生命周期 关键属性 过期策略
授权码 (authorization_code) 单次使用,默认60秒 code, client_id, redirect_uri, scope, code_challenge TTL过期 + 使用后立即删除
访问令牌 (access_token) 可配置,默认3600秒 token, client_id, scope, token_type, expiry TTL过期 + 撤销标记
刷新令牌 (refresh_token) 可配置,默认86400秒 token, client_id, scope, rotation_count TTL过期 + 轮换策略
客户端注册 (client) 持久化,直到显式删除 client_id, client_secret, redirect_uris, grant_types 无自动过期,支持软删除

状态存储建议使用内存中的并发安全Map(如Go的sync.Map或Java的ConcurrentHashMap),配合定时清理协程处理过期状态。对于分布式测试场景,可扩展为Redis后端,但需注意序列化与网络延迟对Mock性能的影响。

避坑提示: 授权码的“单次使用”约束必须在并发场景下严格保证。建议使用CAS(Compare-And-Swap)操作或分布式锁,防止同一授权码被重复兑换令牌。否则会导致测试结果不可靠。

4.4 延迟模拟设计

延迟模拟用于测试客户端在非理想网络条件下的行为。设计时需支持以下三种延迟模式:

  • 固定延迟 — 每个请求等待固定毫秒数,适用于模拟稳定的网络延迟基线。
  • 随机延迟 — 在[min, max]区间内均匀或正态分布随机延迟,模拟真实网络抖动。
  • 阶梯延迟 — 按请求次数递增延迟(如第1次0ms,第2次100ms,第3次500ms),用于测试重试与退避策略。

延迟模拟的配置可通过管理端点动态注入,支持按端点、按client_id、按scope进行精细化控制。以下为延迟配置的JSON示例:

{
  "endpoint": "/oauth2/token",
  "delay_mode": "random",
  "min_ms": 100,
  "max_ms": 500,
  "distribution": "uniform",
  "apply_to": {
    "client_id": "test-client-01",
    "scope": "openid profile"
  }
}
可观测性集成: 每次延迟模拟应在Span中记录实际等待时间、配置的延迟模式与参数。通过Tracing可以直观看到延迟注入对客户端行为的影响,便于分析超时与重试逻辑。

4.5 错误注入设计

错误注入是Mock服务区别于普通Stub的关键能力。设计时需覆盖以下错误类型:

错误类别 示例 注入方式 可观测性记录
HTTP协议错误 500 Internal Server Error, 503 Service Unavailable 按端点配置返回指定状态码 记录错误码、触发规则、请求ID
OAuth2协议错误 invalid_grant, invalid_client, unauthorized_client 按条件匹配返回标准错误响应 记录error、error_description、状态码
网络级异常 连接超时、连接重置、TLS握手失败 在Socket层注入异常(需代理模式) 记录异常类型、注入点、持续时间
数据异常 返回格式错误的JWT、缺失字段、过期时间异常 修改响应体结构或字段值 记录原始响应与修改后响应的差异

错误注入规则应支持条件匹配,例如:仅当请求中包含特定scope时返回invalid_scope;仅当令牌过期时间小于30秒时返回令牌无效。这种条件式注入可以模拟真实场景中的边界情况。

测试策略建议: 建议将错误注入规则与测试用例绑定,在测试启动时通过管理端点注入规则,测试结束后自动清理。避免规则残留影响后续测试的独立性。

4.6 Mock服务整体架构

综合以上设计原则,Mock服务的整体架构如下图所示。该架构将协议端点、状态管理、延迟模拟、错误注入与可观测性模块有机整合,形成一个可编程、可观测的OAuth2.0仿真系统。

OAuth2.0 Mock服务整体架构 客户端层 (Client Layer) 测试框架 / 集成测试 / 开发环境应用 API网关 / 路由层 请求分发 | 协议端点路由 | 管理端点隔离 Mock核心引擎 (Core Engine) 授权码流程 令牌管理 客户端注册 JWKS签名 状态管理 (State Manager) 授权码池 | 令牌缓存 | 过期清理 | 并发控制 延迟与错误注入 (Fault Injector) 延迟策略 | 错误规则 | 条件匹配 | 异常模拟 可观测性层 (Observability Layer) Metrics (Prometheus) | Tracing (OpenTelemetry) | Logging (结构化日志) 管理端点配置注入

架构图中各层职责清晰:客户端层发起请求,API网关层负责路由分发与协议端点隔离,核心引擎层处理OAuth2.0协议逻辑,状态管理层维护所有状态对象的生命周期,延迟与错误注入层提供可编程的故障模拟能力,可观测性层负责采集与暴露三支柱数据。各层之间通过明确的接口进行交互,便于独立扩展与测试。

4.7 可观测性集成要点

Mock服务的可观测性设计应贯穿所有模块,而非事后追加。以下是各模块的可观测性集成要点:

  • 请求追踪 — 每个请求生成唯一Trace ID,贯穿网关、引擎、状态管理、错误注入全链路。在Span中记录端点、延迟模式、错误注入规则等上下文信息。
  • 指标暴露 — 暴露请求总数、成功率、延迟分布(P50/P90/P99)、错误注入触发次数、状态对象数量等核心指标。使用Prometheus格式暴露,便于Grafana可视化。
  • 结构化日志 — 每条日志包含timestamp、level、trace_id、endpoint、client_id、error_code等结构化字段。日志级别支持动态调整,便于问题排查时临时开启Debug日志。
  • 健康检查 — 暴露/healthz和/readyz端点,返回各模块的健康状态。状态管理模块应检查过期清理协程是否正常运行,错误注入模块应检查规则引擎是否可用。
实战建议: 在Mock服务启动时,通过环境变量或配置文件加载可观测性配置(如采样率、指标暴露端口、日志输出格式)。避免在运行时频繁修改可观测性配置,以免影响Mock服务本身的稳定性。