防刷核心是后端多层校验:优先用用户ID,辅以设备指纹和IP滑动窗口限流,结合Redis原子操作实现高频防护,同时避免误伤正常用户。

防刷核心是限制单个用户在单位时间内的投票次数,同时兼顾真实性和体验。不能只靠前端限制,必须后端校验;也不能过度拦截,导致正常用户被误伤。
基于用户身份的多层校验
单一维度(比如IP)容易被绕过,应组合使用多种标识:
- 登录态校验:优先依赖用户ID(如Yii::$app->user->id),已登录用户以账号为唯一依据,最可靠
- 设备指纹(可选):服务端生成简易指纹(如User-Agent + IP前两段 + 屏幕宽高哈希),不依赖JS,适合未登录场景
- IP限频兜底:对未登录用户或异常高频请求,按IP+UA做滑动窗口计数,避免恶意扫票
使用Redis实现滑动窗口限流
比数据库查询快,支持原子操作,适合高频写入场景。Yii中可用yii\redis\Connection:
- 键名格式:
vote:uid:{userId}:24h或vote:ip:{ipHash}:1h - 每次投票前用
INCR+EXPIRE保证TTL,或用LPUSH + LLEN + EXPIRE维护时间戳队列 - 示例逻辑:若24小时内该用户已投满3票,直接返回
{"code":403,"msg":"今日投票次数已用完"}
关键业务规则落地建议
防刷不是纯技术问题,需与产品规则对齐:
- 明确“同一用户”定义:是否允许同一账号多设备投票?是否允许家庭宽带多个IP投票?这些要提前约定并写进校验逻辑
- 投票后立即记录日志(含用户ID、IP、UA、时间戳、选项ID),便于事后审计和人工复核
- 对触发阈值的请求,可降级处理(如加入人工审核队列),而非直接拒绝,减少误伤
- 后台提供实时统计面板:查看每小时投票峰值、异常IP列表、高频用户TOP10,方便快速响应
规避常见陷阱
有些看似合理的设计实际效果差:
- 仅校验Referer或Origin——易伪造,不可信
- 前端加随机token再提交——若后端不校验有效性或不绑定会话,形同虚设
- 用Cookie存投票状态——可清除、可伪造,必须服务端主导校验
- 每次投票都查一次数据库——高并发下成瓶颈,必须用缓存+异步落库(如投票成功后发消息队列写DB)


















