真正防止接口被恶意刷量需验证码与后端限流深度耦合;安装mews/captcha扩展,配置并注册服务,前端嵌入防缓存验证码,后端结合CSRF、多层限流(Nginx/设备指纹/业务校验)及Redis异常兜底。

要真正防止接口被恶意刷量,光靠前端按钮禁用或单纯路由限流远远不够——验证码必须和后端限流策略深度耦合,否则攻击者绕过图形验证直接调用API仍可高频刷量。
安装并启用图形验证码扩展包
执行 composer require mews/captcha 安装社区验证库,该包支持 default/flat/mini/inverse 四种样式,无需额外图像处理扩展即可运行。
运行 php artisan vendor:publish --provider="Mews\Captcha\CaptchaServiceProvider" 生成 config/captcha.php 配置文件,确保后续能自定义字体、尺寸与过期时间。
在 config/app.php 的 providers 数组末尾添加 Mews\Captcha\CaptchaServiceProvider::class;aliases 中加入 'Captcha' => Mews\Captcha\Facades\Captcha::class。
这一步漏掉会导致 captcha_src() 辅助函数无法识别,页面加载时抛出 Class 'captcha_src' not found 错误。
在登录/注册页嵌入可刷新验证码
在 Blade 模板中插入以下代码:
onclick 中的 Math.random() 是关键,它强制浏览器不缓存图片 URL,避免用户点击后仍显示旧图。
务必在对应表单中加入 @csrf,否则 Laravel 默认会拒绝请求——验证码校验前 CSRF 验证已失败,根本不会走到 captcha 校验逻辑。
后端验证码校验与限流绑定
在控制器中使用 Validator 验证用户输入:captcha 规则自动调用底层校验逻辑,但仅此还不够。
方法一:对验证码发送接口单独限流
在 SendVerificationCodeController@store 中,用 RateLimiter::attempt() 显式控制频次:if (! RateLimiter::attempt('send-code-'.request('phone'), 3, 60)) { return response(['message' => '发送太频繁,请60秒后再试'], 429); }
【注意:key 中不能含 +、空格等非法字符,手机号需提前过滤】
方法二:重写 throttle 中间件 key 提取逻辑
在 app/Http/Middleware/ThrottleRequestsWithCaptcha.php 中继承 ThrottleRequests,覆盖 resolveRequestSignature() 方法,提取 phone 或 email 参数作为限流主体,避免默认按完整 URL 计数导致失效。
高并发场景下分层设防配置
第一步:Nginx 层做漏桶限流,拦截 90% 以上洪峰流量
在 server 块中添加:limit_req_zone $binary_remote_addr zone=api burst=10 nodelay;location /api/code { limit_req zone=api; }
第二步:Laravel 层按设备指纹精细化限流
在 AppServiceProvider@boot 中注册:RateLimiter::for('api-by-device', function (Request $request) { return Limit::perMinute(5)->by($request->header('X-Device-ID') ?: $request->ip()); });
路由中绑定:->middleware('throttle:api-by-device')
第三步:业务层兜底——验证码提交时双重校验
先 check('captcha') → 再校验 Redis 中该手机号是否在 5 分钟内已成功验证过 → 若存在则直接放行,避免重复消耗短信额度。
Redis 连接异常时,RateLimiter::attempt() 会抛出 ConnectionException,必须 try-catch 并 fallback 到日志告警+拒绝请求,【绝不可静默跳过限流】











