beliven-it/laravel-lockout
Composer 安装命令:
composer require beliven-it/laravel-lockout
包简介
A small Laravel package to lock out a user after X failed login attempts.
README 文档
README
A small, opinionated Laravel package that locks accounts after repeated failed login attempts. It provides both in-memory throttling (cache counters) and optional persistent locks stored in the database, plus unlock notifications via a temporary signed link.
Quick Start (Usage)
These steps get you from install to a working lockout flow in your app.
- Install with Composer:
composer require beliven-it/laravel-lockout
- Publish configuration and migration stubs:
php artisan vendor:publish --tag="lockout-config" php artisan vendor:publish --tag="lockout-migrations" php artisan migrate
- Add the trait to your authentication model (e.g.
App\Models\User) and implement theLockableModelcontract:
use Beliven\Lockout\Traits\HasLockout; use Beliven\Lockout\Contracts\LockableModel; class User extends Authenticatable implements LockableModel { use HasLockout; }
- Protect your login route with the middleware:
use Beliven\Lockout\Http\Middleware\EnsureUserIsNotLocked; Route::post('/login', [LoginController::class, 'login']) ->middleware(EnsureUserIsNotLocked::class);
The package also expose an additional middleware for ensure that locked users cannot access to any routes:
use Beliven\Lockout\Http\Middleware\EnsureUserCannotAccessWhenLocked; Route::middleware([EnsureUserCannotAccessWhenLocked::class])->group(function () { // protected routes for authenticated users });
- Scheduler (recommended)
- The package includes a console command
lockout:pruneto remove old records according to the retention values inconfig('lockout.prune')(prune.lockout_logs_daysandprune.model_lockouts_days). In production you should schedule this command to run regularly via Laravel's scheduler (and ensure your cron runsschedule:run).
Example: register scheduled tasks in routes/console.php
<?php //.... // routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('lockout:prune --force')->dailyAt('02:00')->withoutOverlapping(); // Optional: separate schedules for logs/models, or different cadences. Schedule::command('lockout:prune --only-logs --force')->dailyAt('03:00')->withoutOverlapping(); Schedule::command('lockout:prune --only-model --force')->weekly()->withoutOverlapping();
Tips
- You can override the retention periods at runtime by passing
--days-logsand/or--days-modelsto the command, e.g.php artisan lockout:prune --days-logs=30 --days-models=180 --force. - Ensure
lockout.prune.enabledin your config istrue(default) for the scheduled prune to actually perform deletions.
- Test manually:
- Attempt failing logins for the same identifier until the threshold is reached.
- Inspect the
lockout_logstable and (if configured)model_lockoutsfor persistent locks. - If
unlock_via_notificationis enabled, the package will try to send a temporary signed unlock link to a notifiable model.
Configuration (summary)
Full options are in config/lockout.php. Here are the most important settings and their defaults:
return [ 'login_field' => 'email', 'unlock_via_notification' => true, 'notification_class' => \Beliven\Lockout\Notifications\AccountLocked::class, 'notification_channels' => ['mail'], 'max_attempts' => 5, 'decay_minutes' => 30, 'cache_store' => 'database', 'auto_unlock_hours' => 0, 'unlock_redirect_route' => 'login', 'unlock_link_minutes' => 1440, 'logout_on_login' => false, 'prune' => [ 'enabled' => true, 'lockout_logs_days' => 90, 'model_lockouts_days' => 365, ], ];
Recommendation:
- Use a durable cache (Redis or database) in production for counters (
cache_store) so counters persist across processes and restarts.
Unlock route & controller
The package generates signed unlock links using the route name lockout.unlock.
If you publish the package routes or want to register the route yourself, ensure a route with that name exists and points to the unlock controller:
use Beliven\Lockout\Http\Controllers\UnlockController; Route::get('/lockout/unlock', UnlockController::class)->name('lockout.unlock');
The unlock link is temporary and validates the signature before performing the unlock action. The link lifetime is configurable via the lockout.unlock_link_minutes configuration key (default: 1440 minutes, i.e. 24 hours).
--
Logout on Login
If you want to ensure that when a locked user attempts to log in, any existing sessions are terminated, you can enable the logout_on_login configuration option. This is particularly useful in scenarios where you want to prevent locked users from maintaining active sessions.
Also, is useful in scenario like third party admin panels (e.g. Filament, Nova) where you can't easily add the lockout middleware to the login route.
You can enable this feature by setting the logout_on_login option to true in your config/lockout.php file:
'logout_on_login' => true,
You can also override the logoutAfterLogin method on your model that uses the HasLockout trait to customize the logout behavior further. The default behavior is:
public function logoutOnLockout(?string $guard = null): bool { // Default common case behavior // (to be overridden in concrete models if needed) Auth::guard($guard)->logout(); session()->invalidate(); session()->regenerateToken(); return true; }
API Reference (quick)
Use the Lockout facade or the Beliven\Lockout\Lockout service from the container.
Lockout::getAttempts(string $id): int— return attempts count for identifierLockout::incrementAttempts(string $id): void— increment attempts counterLockout::hasTooManyAttempts(string $id): bool— whether attempts >= thresholdLockout::attemptLockout(string $id, object $data): bool— increment, create log, dispatch EntityLocked when threshold reached; returns whether locked after callLockout::attemptSendLockoutNotification(string $id, object $data): void— send the unlock notification if enabled & model is notifiableLockout::getLoginField(): stringLockout::getLoginModelClass(): stringLockout::getLoginModel(string $identifier): ?Model— tries to resolve a model from configured providerLockout::lockModel(Model $model, array $options = []): ?ModelLockout— create persistent lock via relationLockout::unlockModel(Model $model, array $options = []): ?ModelLockout— mark lock as unlocked, clear attempts, dispatch EntityUnlockedLockout::clearAttempts(string $id): void— clear cached counter
These helpers are also used internally by the HasLockout trait; your models can call $model->lock() and $model->unlock() which delegate to the service.
Customization & Extensibility
- Replace notification class: set
lockout.notification_classto your own class implementing the same constructor signature (identifier, duration, url). - Change notification channels: set
lockout.notification_channelsto e.g.['mail', 'database']. - Replace or extend listeners: bind your own listeners for
EntityLocked/EntityUnlockedin yourEventServiceProvider. - Replace login model resolver: set
auth.providers.users.modelto change how the package resolves a model for an identifier.
Example: custom notification class in config/lockout.php:
'notification_class' => App\Notifications\MyAccountLocked::class,
Troubleshooting
- Notifications not sent:
- Ensure
unlock_via_notificationistrue. - The resolved model must implement
notify()(useNotifiabletrait). - Verify your mail driver and config (MAIL_* env vars).
- Ensure
- Counters not behaving:
- Check
cache_storeconfig; if usingarraystore counters reset on each request-run. Useredisordatabasein production.
- Check
- Signed unlock links failing:
- Ensure your app URL / APP_KEY is set and consistent.
- Check the route name
lockout.unlockand that the route is reachable.
- Coverage / testing:
- To generate coverage locally you need Xdebug or phpdbg enabled. Example:
- With Xdebug:
php -d xdebug.mode=coverage vendor/bin/pest --coverage --coverage-text - With phpdbg:
phpdbg -qrr vendor/bin/pest --coverage --coverage-text
- With Xdebug:
- On CI use a matrix image with Xdebug enabled.
- To generate coverage locally you need Xdebug or phpdbg enabled. Example:
Developing (run tests, coverage & local development)
If you're working on the package itself locally:
- Install dev dependencies:
composer install --dev
- Run tests:
vendor/bin/pest
- Generate coverage (requires Xdebug or phpdbg):
# Xdebug php -d xdebug.mode=coverage vendor/bin/pest --coverage --coverage-text --coverage-clover=coverage.xml # phpdbg phpdbg -qrr vendor/bin/pest --coverage --coverage-text --coverage-clover=coverage.xml
- Useful notes:
- Use
config()->set(...)in tests to override settings (examples in tests). - Database migrations for tests are in the test bootstrap; if adding schema changes, update migration stubs and test setup.
- To run a single test file with Pest:
vendor/bin/pest tests/Unit/SomeTest.php
Events & Examples
EntityLocked— fired when threshold is reached. Default listener may create a persistent lock and/or send notification.EntityUnlocked— fired when a persistent lock is cleared. Useful to log unlocks or notify admins.
Example listener registration is done via your EventServiceProvider if you need to override defaults.
Example. Prevent login on filament
// todo
Example: Laravel Nova Action
If you use Laravel Nova, you can expose an Action that lets administrators unlock a user directly from the Nova resource. The Action can resolve the model and call the trait helper $model->unlock(...), which delegates to the package service.
Example Nova Action (concise):
<?php namespace App\Nova\Actions; use Laravel\Nova\Actions\Action; use Laravel\Nova\Fields\ActionFields; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; class UnlockUser extends Action implements ShouldQueue { use Queueable; public function name() { return 'Unlock account'; } /** * Handle the action for the given models. * * @param \Laravel\Nova\Fields\ActionFields $fields * @param \Illuminate\Support\Collection $models */ public function handle(ActionFields $fields, $models) { foreach ($models as $model) { // call the HasLockout trait helper which delegates to the Lockout service // you can pass optional metadata like reason/actor/requestData $model->unlock([ 'reason' => 'Unlocked by admin via Nova', 'actor' => auth()->user()?->id ?? null, ]); } return Action::message('Selected accounts have been unlocked.'); } public function fields() { return []; } }
Add the action to your Nova Resource (e.g. User resource):
// in app/Nova/User.php public function actions(Request $request) { return [ new \App\Nova\Actions\UnlockUser, ]; }
Notes:
- The Nova Action calls the model method
unlock()directly; the trait delegates to the package service so the unlock behaviour (clearing attempts, dispatching events) remains consistent. - If you need to customize the service used by the trait, you can override
resolveLockoutService()on your model (see Customization & Extensibility).
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.
beliven-it/laravel-lockout 适用场景与选型建议
beliven-it/laravel-lockout 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 34 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 11 月 02 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「Beliven」 「laravel-lockout」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 beliven-it/laravel-lockout 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 beliven-it/laravel-lockout 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 beliven-it/laravel-lockout 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A simple and customizable package that adds password expiration functionality to your Laravel applications, enhancing user security with regular password rotation
Alfabank REST API integration
A small package for scan / import / export translations
Laravel package for Accurate Online API integration.
Shared RCX Laravel DataTables UI and configuration helpers.
Boot a Laravel project on any machine with one command: app:serve installs missing tools (PHP, Node, Composer, Herd, Docker), creates .env, sets up the database, runs migrations, builds assets, starts a queue worker and serves via Herd, Sail or artisan serve; app:down cleanly stops everything it sta
统计信息
- 总下载量: 34
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 19
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-11-02
