承接 beliven-it/laravel-lockout 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

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

Laravel Lockout banner


Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

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.

  1. Install with Composer:
composer require beliven-it/laravel-lockout
  1. Publish configuration and migration stubs:
php artisan vendor:publish --tag="lockout-config"
php artisan vendor:publish --tag="lockout-migrations"
php artisan migrate
  1. Add the trait to your authentication model (e.g. App\Models\User) and implement the LockableModel contract:
use Beliven\Lockout\Traits\HasLockout;
use Beliven\Lockout\Contracts\LockableModel;

class User extends Authenticatable implements LockableModel
{
    use HasLockout;
}
  1. 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
});
  1. Scheduler (recommended)
  • The package includes a console command lockout:prune to remove old records according to the retention values in config('lockout.prune') (prune.lockout_logs_days and prune.model_lockouts_days). In production you should schedule this command to run regularly via Laravel's scheduler (and ensure your cron runs schedule: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-logs and/or --days-models to the command, e.g. php artisan lockout:prune --days-logs=30 --days-models=180 --force.
  • Ensure lockout.prune.enabled in your config is true (default) for the scheduled prune to actually perform deletions.
  1. Test manually:
  • Attempt failing logins for the same identifier until the threshold is reached.
  • Inspect the lockout_logs table and (if configured) model_lockouts for persistent locks.
  • If unlock_via_notification is 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 identifier
  • Lockout::incrementAttempts(string $id): void — increment attempts counter
  • Lockout::hasTooManyAttempts(string $id): bool — whether attempts >= threshold
  • Lockout::attemptLockout(string $id, object $data): bool — increment, create log, dispatch EntityLocked when threshold reached; returns whether locked after call
  • Lockout::attemptSendLockoutNotification(string $id, object $data): void — send the unlock notification if enabled & model is notifiable
  • Lockout::getLoginField(): string
  • Lockout::getLoginModelClass(): string
  • Lockout::getLoginModel(string $identifier): ?Model — tries to resolve a model from configured provider
  • Lockout::lockModel(Model $model, array $options = []): ?ModelLockout — create persistent lock via relation
  • Lockout::unlockModel(Model $model, array $options = []): ?ModelLockout — mark lock as unlocked, clear attempts, dispatch EntityUnlocked
  • Lockout::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_class to your own class implementing the same constructor signature (identifier, duration, url).
  • Change notification channels: set lockout.notification_channels to e.g. ['mail', 'database'].
  • Replace or extend listeners: bind your own listeners for EntityLocked / EntityUnlocked in your EventServiceProvider.
  • Replace login model resolver: set auth.providers.users.model to 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_notification is true.
    • The resolved model must implement notify() (use Notifiable trait).
    • Verify your mail driver and config (MAIL_* env vars).
  • Counters not behaving:
    • Check cache_store config; if using array store counters reset on each request-run. Use redis or database in production.
  • Signed unlock links failing:
    • Ensure your app URL / APP_KEY is set and consistent.
    • Check the route name lockout.unlock and 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
    • On CI use a matrix image with Xdebug enabled.

Developing (run tests, coverage & local development)

If you're working on the package itself locally:

  1. Install dev dependencies:
composer install --dev
  1. Run tests:
vendor/bin/pest
  1. 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
  1. 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 我们能提供哪些服务?
定制开发 / 二次开发

基于 beliven-it/laravel-lockout 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 34
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 19
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-11-02