Skip to content

Next.js 高级架构

Next.js Webhook 如何做签名校验、幂等和重放防护?

Webhook 是外部系统主动调用的写入口,不能只看一个 secret 或返回 200,要校验原文、时间、事件和处理状态。

适合阶段:资深全栈 / 安全面核心能力:Webhook · HMAC · Idempotency

面试官想考什么

  • 为什么签名必须基于原始 body? 考察解析顺序。
  • 如何防止同一事件重复处理? 考察幂等。
  • Webhook 处理很慢怎么办? 考察快速确认与异步 job。
  • 签名校验通过就能直接改数据吗? 考察事件来源和业务校验。

一句话回答

text
Route Handler 先读取原始 body,按供应商协议做常量时间签名校验、时间窗和事件 id 去重,再快速落库/入队返回成功;真正业务处理异步执行并可重试,不能把签名通过等同于业务操作可无条件执行。

面试回答详解

1. 接收链路

text
raw body -> signature/timestamp verify -> eventId dedupe
-> persist event -> enqueue -> 202/2xx

JSON parse 后重新 stringify 可能改变空格、顺序和编码,导致签名不一致。

2. 重放防护

签名通常结合 timestamp 计算,服务端限制时间窗口并拒绝过期事件。事件 id 建唯一约束;相同事件再次到达时返回已处理状态,不重复副作用。

3. 异步处理

Route Handler 不应等待长时间业务处理。先保存原始事件摘要和校验结果,再投递 job。Worker 使用状态机、租约、checkpoint 和重试策略;不可重试错误进入人工队列。

4. 业务校验

验证供应商账户、事件类型、资源版本和租户映射。外部事件可能乱序,使用事件版本、发生时间或查询供应商当前状态修正最终结果。

5. 观测和隐私

记录 provider、eventId hash、signature result、处理耗时、attempt 和最终状态;不要保存完整支付数据、secret 或敏感 body。

可直接背诵的 30 秒回答

text
Webhook 要先基于原始 body 校验签名和时间窗,再用 eventId 做唯一去重,快速持久化并入队,返回成功;长业务由 Worker 异步执行,处理要幂等、可重试、能处理乱序。签名通过只是来源认证,还要验证事件类型、资源版本、租户和业务状态。

扩展知识

2xx 的含义

返回 2xx 通常表示“已接收”,不一定表示业务完成;契约要和供应商约定,失败是否重试要清楚。

面试官追问链

追问一:为什么要常量时间比较签名?

  • 考察点:侧信道意识。
  • 回答方向:降低通过响应时间逐字猜测签名的风险,使用平台安全比较 API。

追问二:事件已落库但入队失败怎么办?

  • 考察点:可靠投递。
  • 回答方向:使用 outbox/状态扫描补投,不能只返回成功后把消息丢在内存中。

追问三:供应商没有 eventId 怎么办?

  • 考察点:幂等建模。
  • 回答方向:组合 provider、事件类型、资源 id、版本/时间等稳定字段,必要时查询当前状态和设置去重窗口。

推荐阅读

基于 MIT 协议开源