系统要求5秒内同一IP请求超20次即永久封禁,需配置Redis驱动、可信代理及自定义限流策略,通过CheckFlood中间件校验黑名单并前置注册,确保封禁逻辑生效。

当用户在5秒内连续请求接口超过20次,系统必须立即把该IP加入Redis黑名单并拒绝后续所有访问,而不是只做临时限流——这要求限流中间件能自动触发封禁逻辑,且封禁状态需持久化、可人工干预。
配置Redis驱动与可信代理
确认.env中CACHE_DRIVER=redis且REDIS_CLIENT=predis,否则ThrottleRequests中间件计数器在多进程下失效;运行php artisan tinker执行cache()->store('redis')->get('test')验证连接成功。
打开App\Http\Middleware\TrustProxies,将$proxies设为['127.0.0.1', '10.0.0.0/8'],确保$request->ip()返回真实客户端IP而非Nginx内网地址。
【不配可信代理会导致$request->ip()始终是127.0.0.1,所有用户被误判为同一IP而集体封禁】
定义命名限流策略并嵌入封禁逻辑
在App\Providers\AppServiceProvider的boot()方法中注册名为api-flood的策略:
RateLimiter::define('api-flood', function (Request $request) {
$ip = $request->header('X-Real-IP') ?: $request->ip();
$key = 'flood:'.$ip;
$count = Redis::incr($key);
Redis::expire($key, 5);
if ($count > 20) {
Redis::zadd('ip:black:list', time(), $ip);
Redis::expire('ip:black:list', 86400);
}
return Limit::perMinute(20)->by($ip);
});
注意:此处用Redis::incr而非RateLimiter::attempt(),因后者无法在超限时主动写入黑名单;$key过期时间必须与判断窗口一致(5秒),否则计数残留导致误判。
编写防刷中间件CheckFlood
执行php artisan make:middleware CheckFlood创建中间件。
在handle方法中插入三步校验:
第一步:获取真实IP,优先取X-Real-IP头, fallback到X-Forwarded-For首段,最后用$request->ip()兜底。
第二步:检查Redis有序集合ip:black:list中是否存在该IP,命令为Redis::zscore('ip:black:list', $ip) !== null。
第三步:若存在则直接返回JSON响应["code"=>403,"msg"=>"IP已被封禁"]并终止请求;否则放行至下一中间件。
将CheckFlood注册到app/Http/Kernel.php的$middlewareGroups['api']数组头部,确保它在throttle之前执行。
路由绑定与生效验证
在api.php中定义测试路由:Route::post('/test-flood', [TestController::class, 'flood'])->middleware(['throttle:api-flood', 'check.flood']);
用curl循环请求:for i in {1..25}; do curl -X POST http://localhost/api/test-flood; done
第21次起响应变为403,同时redis-cli zrange 'ip:black:list' 0 -1可查到对应IP已写入。


















