主题
Next.js 高级架构
Next.js Webhook 如何做签名校验、幂等和重放防护?
Webhook 是外部系统主动调用的写入口,不能只看一个 secret 或返回 200,要校验原文、时间、事件和处理状态。
面试官想考什么
- 为什么签名必须基于原始 body? 考察解析顺序。
- 如何防止同一事件重复处理? 考察幂等。
- Webhook 处理很慢怎么办? 考察快速确认与异步 job。
- 签名校验通过就能直接改数据吗? 考察事件来源和业务校验。
一句话回答
text
Route Handler 先读取原始 body,按供应商协议做常量时间签名校验、时间窗和事件 id 去重,再快速落库/入队返回成功;真正业务处理异步执行并可重试,不能把签名通过等同于业务操作可无条件执行。面试回答详解
1. 接收链路
text
raw body -> signature/timestamp verify -> eventId dedupe
-> persist event -> enqueue -> 202/2xxJSON 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、版本/时间等稳定字段,必要时查询当前状态和设置去重窗口。