ToC 系统中的请求幂等、安全签名与防重复提交架构设计
前言
很多程序员第一次做互联网系统时,都会遇到一个问题:
用户重复提交。
例如:
- 连续点击按钮
- 网络卡顿重试
- APP 弱网重发
- 浏览器重复请求
- 恶意脚本攻击
- MQ 重复消费
刚开始:
很多人会认为:
"前端按钮 disabled 一下就好了"
但真正做大型 ToC 系统后会发现:
"重复请求"只是表面问题。
真正的问题其实是:
"互联网环境本身是不可靠的"
你不能假设:
- 请求只会发送一次
- 网络一定稳定
- 用户一定正常操作
- 客户端不会被破解
- 没有恶意攻击
因此:
现代互联网系统真正核心的能力之一是:
幂等设计
也就是:
"即使请求重复执行,结果也保持一致"
一、为什么 ToC 更容易出现重复请求
ToC 和 ToB 最大区别:
不是技术。
而是:
用户行为模型。
ToB 的特点
ToB:
例如:
- ERP
- OA
- CRM
- 财务系统
特点:
- 用户少
- 页面复杂
- 编辑时间长
- 状态生命周期长
用户:
通常:
text
打开页面
→ 真正准备操作
因此:
很多 ToB 系统:
适合:
text
create draft
→ update
模式。
ToC 的特点
ToC:
例如:
- 电商
- 社交
- 短视频
- 点赞
- 评论
- 支付
特点:
- 用户量巨大
- 请求量巨大
- 网络复杂
- 用户行为随机
- 对延迟极其敏感
用户:
经常:
text
点一下
没反应
再点10下
因此:
ToC 的核心问题:
不是:
"业务状态复杂"
而是:
"高并发与不可靠网络"
二、为什么前端 loading 非常重要
很多程序员:
低估了:
前端交互的重要性。
实际上:
大量重复请求:
根本不是:
text
真正并发
而是:
"用户焦虑"
例如:
页面卡顿 500ms
用户:
text
是不是没点上?
再点一次。
页面卡顿 2 秒
用户:
text
是不是系统死了?
开始疯狂点击。
因此:
现代 ToC:
第一层防御:
通常就是:
loading + disable button
例如:
js
if(loading){
return;
}
loading=true;
处理完成:
js
loading=false;
这种方案:
成本极低。
但收益极高。
三、为什么前端控制不够
前端:
只能解决:
"正常用户误操作"
解决不了:
- 网络重试
- APP 弱网
- 多设备并发
- 恶意攻击
- MQ 重复消费
因此:
真正成熟的系统:
必须:
服务端幂等
四、requestId 的核心思想
现代互联网系统:
非常喜欢:
requestId
也叫:
- nonce
- idempotencyKey
- requestToken
本质:
都是:
请求唯一标识。
核心思想
客户端:
生成:
text
uuid
例如:
json
{
"requestId":"abc123"
}
服务端:
记录:
text
abc123 已处理
重复请求:
直接返回第一次结果。
五、真正成熟的 requestId 设计
很多人:
会犯一个错误:
错误方案
每次请求:
生成新的 requestId。
例如:
js
requestId=randomUUID()
如果:
用户点两次:
就会:
text
两个 requestId
服务端:
根本无法识别重复。
正确方案
一次业务操作:
requestId 必须固定。
例如:
text
用户点击提交
→ requestId=abc
→ 网络失败
→ 重试
→ 仍然 abc
这样:
服务端:
才能识别:
"这是同一次业务"
六、为什么 requestId 不等于安全
这是很多人误解的地方。
很多人认为:
text
有 requestId 就安全了
其实:
完全不是。
requestId 只能解决:
"重复请求"
但:
解决不了:
"伪造请求"
例如:
黑客:
完全可以:
bash
curl /api/pay
然后:
每次:
text
requestId 不一样
系统:
仍然会执行。
因此:
真正的互联网系统:
必须增加:
签名机制
七、为什么需要签名算法
签名算法:
本质上解决的是:
"证明请求来自合法客户端"
标准请求结构
通常:
http
POST /api/order
appId: xxx
timestamp: xxx
nonce: xxx
sign: xxx
各字段作用
appId
标识调用方。
timestamp
防止重放攻击。
例如:
5分钟后:
请求自动失效。
nonce/requestId
防重复请求。
sign
证明:
请求没有被篡改。
八、对称签名与非对称签名
这是非常核心的问题。
1. 对称签名(HMAC)
例如:
text
HMAC-SHA256
特点:
客户端:
和:
服务端:
共享:
text
secretKey
优点
- 快
- CPU 开销小
- 实现简单
- 高并发友好
缺点
如果:
text
secret 泄漏
攻击者:
既能:
text
签名
也能:
text
验签
系统:
直接失守。
2. 非对称签名
例如:
- RSA
- ECDSA
- Ed25519
特点:
客户端:
持有:
text
privateKey
服务端:
持有:
text
publicKey
只有私钥:
能签名。
公钥:
只能验签。
非对称签名最大价值
即使:
服务端被攻击。
攻击者:
也无法伪造客户端签名。
因为:
没有私钥。
九、为什么金融系统喜欢非对称签名
例如:
- 银行
- 数字钱包
- 区块链
- 数字证书
- OAuth
都大量使用:
非对称签名
因为:
它天然适合:
零信任体系
十、为什么 Web 很难真正安全
这是很多人忽略的问题。
浏览器:
本质上:
无法真正保存秘密。
因为:
- JS 可读
- 插件可注入
- XSS 攻击
- 浏览器可调试
所以:
纯 Web 前端:
即使:
使用签名。
也无法做到:
真正安全。
十一、为什么移动端更适合非对称签名
APP:
可以:
- Android Keystore
- iOS Keychain
- Secure Enclave
- TEE
把私钥:
放进:
硬件安全区
因此:
移动端:
比 Web:
更适合:
非对称签名体系。
十二、为什么大型互联网系统最终会变成"多层防御"
真正成熟的系统:
不会依赖:
单一方案。
而是:
多层防御体系。
第一层:前端 UX
例如:
- loading
- disable
- debounce
减少重复点击。
第二层:requestId
保证:
请求级幂等。
第三层:timestamp
防止:
重放攻击。
第四层:签名
防止:
伪造请求。
第五层:状态机
保证:
业务状态合法。
例如:
sql
where status='unpaid'
第六层:唯一索引
数据库最终兜底。
例如:
sql
unique(order_no)
第七层:风控系统
例如:
- IP分析
- 设备分析
- 行为分析
- 点击轨迹
- 地理位置
因为:
真正的大型 ToC:
最后拼的:
其实是:
风控能力
十三、真正高级的互联网架构思想
很多人:
刚开始:
总想:
"阻止重复请求"
但真正成熟的互联网系统:
最终都会变成:
"允许请求重复,但结果保持一致"
因为:
互联网环境:
天然不可靠。
真正稳定的系统:
不是:
"请求绝不重复"
而是:
"即使重复,也不会出错"
这就是:
幂等设计
真正的核心思想。
十四、结语
很多程序员:
刚开始:
学习的是:
- Spring Boot
- Redis
- MQ
- Docker
- Kubernetes
但真正的大型互联网系统:
核心问题:
往往不是:
"技术怎么用"
而是:
"系统如何在不可靠环境下长期稳定运行"
因此:
未来真正重要的能力:
会越来越偏向:
- 幂等设计
- 状态机
- 分布式一致性
- 安全体系
- 风控系统
- 系统演化能力
这才是:
现代互联网架构:
真正的核心竞争力。