Workerman需手动在onMessage中校验timestamp和nonce防重放。检查时间差≤30秒,Redis存nonce并设35秒过期,签名须含timestamp、nonce与密钥,禁用本地内存或文件存nonce,时间戳校验必须用服务端毫秒时间。

Workerman 本身不内置防重放逻辑,必须由你手动在 onMessage 或自定义中间件中校验请求的时效性与唯一性。光靠 HTTPS 或 Token 验证完全无效。
为什么 Workerman 特别容易被重放?
Workerman 常用于长连接场景(如 WebSocket),但默认不维护请求上下文、不强制携带时间戳或随机数;客户端可反复发送同一帧消息,服务端若只校验 token 或 user_id,就会执行多次相同操作。
- WebSocket 消息无天然“过期”机制,不像 HTTP 可依赖短生命周期
- 很多项目把鉴权逻辑写在
onConnect,后续onMessage完全跳过校验 - 前端 JS 可轻易复用旧消息(比如用户点击“确认支付”后,抓包重发)
必须在 onMessage 中校验 timestamp 和 nonce
这是最通用、最低成本的方案。客户端每次发消息都带 timestamp(毫秒级)和 nonce(UUID v4 或 16 字节随机 Base64),服务端做两件事:
- 检查
Math.abs(System.currentTimeMillis() - request.timestamp)是否超过阈值(建议 ≤ 30_000ms) - 用
redis.setex("nonce:" + request.nonce, 35, "1")尝试写入,若返回null(已存在),则拒绝 -
nonce的 Redis 过期时间必须略大于时间窗口(如 35 秒),避免因网络延迟导致误拒
签名必须包含这两项:hmac_sha256(payload + timestamp + nonce + secret_key),否则攻击者可篡改 timestamp 或伪造 nonce。
立即学习“前端免费学习笔记(深入)”;
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
避免用序列号(SequenceNumber)的坑
虽然序列号能杜绝重放,但在 Workerman 多进程部署下极难落地:
- 每个 Worker 进程需共享并原子递增序列号,必须依赖 Redis
INCR+GETSET,性能开销大 - 客户端断线重连后,序列号重置逻辑复杂(要同步服务端最新 seq),容易卡死或跳变
- 如果前端是多个标签页共用一个连接,或用户手动刷新页面,序列号状态就不可控
除非你的业务严格限定单连接、单设备、强状态(如交易终端),否则别碰 seq。
Redis 存 nonce 时要注意的细节
不是所有缓存方案都可靠:
- 别用本地内存(
static array)存 nonce:多 Worker 进程间不共享,重放请求可能落到不同进程绕过校验 - 别用文件存储:高并发下 IO 瓶颈严重,且无法自动过期
- Redis key 命名建议加业务前缀:
"replay:ws:" + user_id + ":" + nonce,方便按用户清理或监控 - 务必设置
EXPIRE,且过期时间 > 时间窗口,否则刚过期的请求可能被漏放行
真正容易被忽略的是:时间戳校验必须用服务端当前毫秒时间,不能用 microtime(true) 或 gettimeofday(),否则纳秒级误差会导致合法请求被拒。

















