Laravel登录失败限流需正确启用LoginThrottles trait并确保调用hasTooManyLoginAttempts(),配置maxAttempts和decayMinutes,使用支持原子操作的缓存驱动,并可重写sendLockoutResponse和throttleKey自定义响应与限流维度。
Laravel 默认就支持登录失败次数限制,靠的是 LoginThrottles 这个 trait,不需要额外装包,但必须正确启用和配置,否则形同虚设。
LoginThrottles 还没生效?常见原因是没在控制器里真正调用限流逻辑——Laravel 的 AuthenticatesUsers trait 虽然引入了 LoginThrottles,但它只在 sendFailedLoginResponse() 里触发计数,而这个方法只有在认证失败后才执行。如果你重写了 login() 或绕过了默认认证流程(比如手写 Auth::attempt() 但没调用 $this->hasTooManyLoginAttempts()),限流就完全不会启动。
LoginController 是否继承自 AuthenticatesUsers
login() 方法却遗漏对 $this->hasTooManyLoginAttempts($request) 的调用throttleLogins 没被设为 false(该属性控制是否启用限流,默认 true)maxAttempts 和 decayMinutes 怎么配才合理?这两个参数定义在 LoginController 中,控制“多少次失败后锁定”和“锁定多久”。它们直接影响用户体验和防爆破效果,不能只看文档默认值。
maxAttempts = 5、decayMinutes = 1:适合开发环境快速验证,但生产环境容易误伤正常用户(比如输错密码+ CapsLock)maxAttempts = 5、decayMinutes = 5 或 10,兼顾安全与体验file 或 redis)存储尝试记录,如果缓存驱动不支持原子操作(如 f
ile 在高并发下可能丢计数),实际限流会不准默认返回 429 状态码并跳转回登录页,但前端往往需要更明确的提示(比如“密码错误 3 次,还剩 2 次机会”或“已被锁定,请 5 分钟后重试”)。你得重写两个方法:
protected function sendLockoutResponse(Request $request)
{
$seconds = $this->limiter()->availableIn(
$this->throttleKey($request)
);
return response()->json([
'message' => '登录失败次数过多,请 '.ceil($seconds / 60).' 分钟后重试',
'locked_until' => now()->addSeconds($seconds)->toISOString()
], 429);}
protected function hasTooManyLoginAttempts(Request $request)
{
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request),
$this->maxAttempts(),
$this->decayMinutes() * 60
);
}
关键点:$this->limiter() 返回的是 Laravel 内置的 RateLimiter 实例;$this->throttleKey($request) 默认用 ip|email 组合做键名,你也可以重写它来改成仅按邮箱限流(防同一账号多 IP 尝试)。
最容易被忽略的是缓存驱动选择和 throttleKey 的语义——如果你用 file 缓存又部署多台服务器,限流会失效;如果想按用户封禁而非 IP,就得改键名逻辑,否则攻击者换 IP 就能绕过。这些细节不调,开着 LoginThrottles 也等于没开。