Skip to content

本地验签与服务端交换 ​

本页解释 wicket 三个无状态验证包背后的两种机制,以及如何按手里凭据的形态选择。

三个包的共同点:都是业务后端直接调用的普通 Go 包,不经过 wicket 的中间件、不读写任何 store、也不依赖 DI 容器;输入一个移动端或前端拿到的凭据串,输出一份已核实的身份或一个可分类的错误。

两种机制 ​

本地验签(oidcverify):凭据是一个带签名、自包含的 ID Token(JWT)。后端用提供者公布的 JWKS 公钥在本地验证签名,再检查 iss、aud、exp 等 claim。公钥会被缓存,所以绝大多数验证不需要出网。

服务端交换(wechatverify、phoneverify):凭据是一个只有厂商自己能解读的短时不透明串(微信授权 code、号码认证 token)。后端带上自己的应用凭据,把它发给厂商接口,由厂商返回身份。每次验证都是一次网络往返。

对比 ​

维度本地验签 oidcverify服务端交换 wechatverify / phoneverify
凭据形态签名 JWT,自带 claims不透明短时串
每次验证是否出网否;只有首次取 JWKS、遇到未知 kid、缓存到期刷新时出网是,每次一个往返
典型延迟本地密码学运算,亚毫秒级取决于到厂商的网络往返
厂商故障的影响公钥已缓存时不受影响厂商不可用即无法验证
后端需持有的机密无,只需公钥有:应用密钥 / 访问密钥
时间判定本方:需注入时钟校验 exp / iat / nbf厂商侧:凭据是否过期由厂商判断
配额与限流无受厂商 QPS 与配额约束,“可重试”是常规分支
同一凭据重复验证可以,结果一致通常不可以,凭据大多一次性,重复交换会失败
错误风格9 个细分哨兵(签名、过期、签发方、受众、nonce……)4 个类别哨兵 + *VendorError 携带厂商原始码

怎么选 ​

规则只有一条:看凭据形态,而不是看厂商名字。

  • 凭据是签名 JWT(Apple 的 identityToken、Google 登录 SDK 的 ID Token、任何标准 OIDC 提供者的 ID Token)→ 用 oidcverify。即使厂商也提供“把 token 发过来我帮你验”的接口,也没有必要用:多一次出网,只会把厂商的故障变成你的故障。
  • 凭据是厂商私有的不透明串 → 只能走服务端交换,因为除了厂商没人能读懂它。

两类共同遵循的约定 ​

无论哪种机制,三个包都采用同一套保守姿态:

  • 失败一律关闭(fail-closed)。任何无法确认身份的情况都返回错误,不存在“验证失败但仍给个部分身份”。
  • 未配置即构造失败。缺少必要配置时,NewVerifier 当场返回错误,而不是等到第一次验证才发现。wechatverify / phoneverify 返回 ErrUnavailable;oidcverify 在签发方或受众为空时返回普通错误。
  • 读不懂的响应按“拒绝”处理。服务端交换中遇到无法识别的厂商返回码,归入 ErrInvalidCredential 而不是 ErrRetryable——把一次读不懂的拒绝当作可重试,可能把它重试成一次意外放行。
  • 调用方取消不算可重试(仅 wechatverify / phoneverify)。ctx 被取消时,错误满足 errors.Is(err, context.Canceled),且不带可重试标记;deadline 到期则同时满足 context.DeadlineExceeded 与 ErrRetryable。oidcverify 不保留上下文错误:取 JWKS 时的取消或超时都表现为 ErrKeyNotFound。
  • 包内不写日志。错误值和配置的字符串形式已对机密脱敏;日志由调用方记录。

对外响应码建议 ​

下表是把验证结果映射为你自己 API 响应的一种常见做法,供参考:

结果建议响应理由
成功200,继续业务流程—
ErrInvalidCredential,或 oidcverify 除 ErrKeyNotFound 外的哨兵401用户提交的凭据不成立
oidcverify.ErrKeyNotFound401 或 503可能是令牌的 kid 确实不存在,也可能是 JWKS 拉取失败;两者无法仅凭哨兵区分,建议告警并结合日志判断
ErrRetryable503,可带 Retry-After暂时无法判定,客户端可稍后重试
ErrUnavailable503,并告警本方配置或权限问题,应由运维处理
ErrMalformedResponse502厂商返回了无法理解的内容
context.Canceled不必响应调用方已放弃

移动端登录的典型时序(服务端交换) ​

text
App ──(厂商 SDK)──> 厂商:用户授权
App <───────────── 厂商:一次性 code / token
App ──(code)──────> 你的后端
                    你的后端 ──(code + 应用凭据)──> 厂商接口
                    你的后端 <──(unionid / 手机号)── 厂商接口
App <──(你的会话)── 你的后端:按身份查找或创建用户

应用密钥只存在于你的后端(以及 wicket 所在进程),不必下发给 App,也不必分发到每个业务系统。

相关阅读 ​

以 Apache License 2.0 发布 · 文档内容 © 2026 Wicket 作者