Laravel 12 登录失败锁定账户、IP及解锁实现方案
1. 数据库准备
首先,我们需要在用户表中添加相关字段:
// 创建迁移文件
php artisan make:migration add_lock_fields_to_users_table
// 迁移文件内容
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->integer('login_attempts')->default(0);
$table->timestamp('locked_at')->nullable();
$table->string('locked_reason')->nullable();
});
// 创建IP锁定表
Schema::create('ip_lockouts', function (Blueprint $table) {
$table->id();
$table->string('ip_address');
$table->integer('attempts')->default(0);
$table->timestamp('locked_at')->nullable();
$table->timestamp('unlock_at')->nullable();
$table->timestamps();
$table->index('ip_address');
});
}
2. 实现账户锁定逻辑
2.1 创建登录失败监听器
php artisan make:listener RecordFailedLoginAttempt
// app/Listeners/RecordFailedLoginAttempt.php
public function handle(Failed $event)
{
$user = $event->user;
$ip = request()->ip();
// 更新用户失败尝试次数
if ($user) {
$user->increment('login_attempts');
// 检查是否需要锁定账户
if ($user->login_attempts >= config('auth.lockout.max_attempts')) {
$user->update([
'locked_at' => now(),
'locked_reason' => 'Too many failed login attempts',
]);
// 触发账户锁定事件
event(new AccountLocked($user));
}
}
// 记录IP失败尝试
$ipLockout = IpLockout::firstOrCreate(['ip_address' => $ip]);
$ipLockout->increment('attempts');
// 检查是否需要锁定IP
if ($ipLockout->attempts >= config('auth.lockout.ip_max_attempts')) {
$ipLockout->update([
'locked_at' => now(),
'unlock_at' => now()->addMinutes(config('auth.lockout.ip_lockout_minutes'))
]);
// 触发IP锁定事件
event(new IpLocked($ip));
}
}
2.2 创建账户锁定中间件
php artisan make:middleware CheckAccountStatus
// app/Http/Middleware/CheckAccountStatus.php
public function handle($request, Closure $next)
{
if (auth()->check()) {
$user = auth()->user();
// 检查账户是否被锁定
if ($user->locked_at) {
auth()->logout();
return redirect()->route('login')
->with('error', '您的账户已被锁定。' . ($user->locked_reason ? '原因: ' . $user->locked_reason : ''));
}
}
// 检查IP是否被锁定
$ipLockout = IpLockout::where('ip_address', $request->ip())
->whereNotNull('locked_at')
->where('unlock_at', '>', now())
->first();
if ($ipLockout) {
return redirect()->route('login')
->with('error', '您的IP已被锁定,请 ' . $ipLockout->unlock_at->diffForHumans() . ' 后再试');
}
return $next($request);
}
3. 实现解锁功能
3.1 自动解锁
创建定时任务检查并解锁过期的锁定:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
// 解锁过期账户
User::whereNotNull('locked_at')
->where('locked_at', '<', now()->subMinutes(config('auth.lockout.lockout_minutes')))
->update([
'locked_at' => null,
'locked_reason' => null,
'login_attempts' => 0
]);
// 解锁过期IP
IpLockout::whereNotNull('locked_at')
->where('unlock_at', '<', now())
->update([
'locked_at' => null,
'unlock_at' => null,
'attempts' => 0
]);
})->everyFiveMinutes();
}
3.2 管理员手动解锁
创建解锁控制器:
php artisan make:controller Admin/UnlockController
// app/Http/Controllers/Admin/UnlockController.php
public function unlockAccount(User $user)
{
$user->update([
'locked_at' => null,
'locked_reason' => null,
'login_attempts' => 0
]);
return back()->with('success', '账户已解锁');
}
public function unlockIp($ip)
{
IpLockout::where('ip_address', $ip)->update([
'locked_at' => null,
'unlock_at' => null,
'attempts' => 0
]);
return back()->with('success', 'IP已解锁');
}
4. 配置
在 config/auth.php
中添加锁定配置:
'lockout' => [
'max_attempts' => 5, // 账户锁定前最大尝试次数
'lockout_minutes' => 30, // 账户锁定时间(分钟)
'ip_max_attempts' => 10, // IP锁定前最大尝试次数
'ip_lockout_minutes' => 60, // IP锁定时间(分钟)
],
5. 注册中间件和事件
在 app/Http/Kernel.php
中注册中间件:
protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\CheckAccountStatus::class,
],
];
注册事件监听器:
// app/Providers/EventServiceProvider.php
protected $listen = [
Failed::class => [
RecordFailedLoginAttempt::class,
],
AccountLocked::class => [
SendAccountLockedNotification::class,
],
IpLocked::class => [
SendIpLockedNotification::class,
],
];
6. 前端显示
在登录视图中显示锁定信息:
@if(session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
@if($errors->has('locked'))
<div class="alert alert-danger">
{{ $errors->first('locked') }}
</div>
@endif
7. 测试
可以使用以下方法测试功能:
- 故意输入错误密码达到锁定阈值
- 检查数据库锁定状态
- 尝试登录已锁定账户/IP
- 等待自动解锁或使用管理员解锁
- 验证解锁后能否正常登录
8. 扩展功能建议
- 解锁验证码: 在接近锁定阈值时要求验证码
- 多因素认证: 锁定后通过邮箱或手机验证解锁
- IP白名单: 允许特定IP绕过锁定限制
- 锁定日志: 记录所有锁定和解锁操作
- 通知系统: 锁定后发送邮件通知管理员
这个实现方案提供了完整的账户和IP锁定机制,包括自动和手动解锁功能,可以根据实际需求进行调整。