
何回も試行すると一定時間たたないと実行できないというやつです。
ログインを例に概念的に書いているのでそのままでは動かない…と思う💦
ふわっと参考までに🐱✨
もくじ
limit_login_attemptsテーブル
| id | user_id | login_attempt | attempt_time | allow_time |
| 1 | 1 | 2 | 2020-06-22 17:29:38 | null |
| 2 | 2 | 0 | 2020-06-22 18:29:38 | 2020-06-22 19:29:38 |
- 試行するたびにlogin_attemptの回数をカウント
- login_attemptが3回以上の時にロックアウト
・ログインでallow_timeに現在日時から1時間を加えた日時を設定
・allow_timeを迎えるまでログインさせない。
・login_attemptを0にリセット - ログイン試行した時にattempt_timeが現在日時より1時間以上過去の場合
・login_attemptをリセットする
・通常の試行を行う
login_attemptを1に繰り上げる。attempt_timeの更新
マイグレーションファイル
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateLimitLoginAttempts extends Migration
{
const TABLE = 'limit_login_attempts';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable(self::TABLE)) {
return;
}
Schema::create(self::TABLE, function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->Integer('login_attempt')->unsigned()->default(0)->comment('試行回数');
$table->dateTime('attempt_time')->nullable()->comment('最終ログイン失敗日時');
$table->dateTime('allow_time')->nullable()->comment('ロック解除日時');
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists(self::TABLE);
}
}
インターフェイス
<?php
namespace App\Repositories\Login;
use App\Repositories\Traits\ResourceConstructInterface;
interface LimitLoginAttemptInterface extends ResourceConstructInterface
{
public function store(int $user_id);
public function firstOrCreateUserId(int $user_id);
public function lock(int $user_id);
}
リポジトリ
<?php
namespace App\Repositories\Login;
use App\Repositories\AbstractRepository;
use App\Entities\LimitLoginAttempt;
use Carbon\Carbon;
class LimitLoginAttemptRepository extends AbstractRepository implements LimitLoginAttemptInterface
{
const RESET_COUNT = 0;
const ADD_HOUR_FOR_LOCK = 1;
public function __construct(LoginAttempt $resource)
{
$this->resource = $resource;
}
/**
* user_idを指定してインスタンスを返却
*
* @param int $user_id
* @return object(App\Entities\LoginAttempt)
*/
public function firstOrCreateUserId(int $user_id): LoginAttempt
{
return ($this->resource->firstOrCreate(['user_id' => $user_id]));
}
/**
* 試行回数を繰り上げて保存
*
* @param int $user_id
*/
public function store(int $user_id): void
{
$now = Carbon::now();
$login_attempt = $this->resource->where('user_id', '=', $user_id)->first();
$attempt_time = $login_attempt->attempt_time;
$attempt_time = new Carbon($attempt_time);
$attempt_time->addHour(self::ADD_HOUR_FOR_LOCK);
// 十分に最後の設定変更から時間が空いている場合は試行回数をリセット
if ($attempt_time < $now) {
$login_attempt->login_attempt = self::RESET_COUNT;
}
$login_attempt->login_attempt++;
$login_attempt->attempt_time = Carbon::now();
$login_attempt->save();
}
/**
* 試行回数を超えた時のロック処理
*
* @param int $user_id
*/
public function lock(int $user_id): void
{
$now_lock = Carbon::now();
$login_attempt = $this->resource->where('user_id', '=', $user_id)->first();
$login_attempt->login_attempt = self::RESET_COUNT;
$login_attempt->attempt_time = Carbon::now();
$login_attempt->allow_time = $now_lock->addHour(self::ADD_HOUR_FOR_LOCK);// 制限解除する日時設定
$login_attempt->save();
}
}
app/Http/Providers/RepositoryServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register services.
*
* @return void
*/
public function register()
{
・・・
$this->app->bind(
\App\Repositories\Login\LimitLoginAttemptInterface::class,
\App\Repositories\Login\LimitLoginAttemptRepository::class
);
}
}
サービス
<?php
namespace App\Services;
use App\Repositories\User\UserInterface;
use App\Repositories\Login\LoginAttemptInterface;
use Carbon\Carbon;
class LoginService extends AbstractClass
{
const LIMIT_LOGIN_ATTEMPT = 3;
const LIMIT_LOGIN_ATTEMPT_INIT = 0;
public function __construct(
UserInterface $r_c,
LimitLoginAttemptInterface $r_la
) {
parent::__construct();
$this->r_c = $r_c;
$this->r_la = $r_la;
}
/**
* 実行
*
* @param int $user_id
* @return void
*/
public function execute(int $user_id)
{
if (!is_numeric($user_id)) {
$this->error('指定された user_id は無効です。整数を入力してください。user_id:' . $user_id);
exit;
}
// ログイン
if ((isset($user_id))) {
$this->userLogin($user_id);
}
abort(422, '無効な値が投げられました');
}
/**
* ログイン試行回数バリデーションを通してログイン
*
* @param int $user_id
*/
public function userLogin($user_id)
{
try {
// 試行回数チェック
$login_attempt = $this->r_la->firstOrCreateUserId($user_id);
$now_date = Carbon::now();
if ($login_attempt->change_attempt >= self::LIMIT_LOGIN_ATTEMPT) {
$this->r_la->lock($user_id);
$this->error('試行制限がかかりました。1時間後に改めて実行してください');
exit;
}
//ログイン実行する
\Exec::Login();
// ドメイン変更試行回数を加算
$this->r_la->store($user_id);
} catch (\Exception $e) {
\Log::error($e->getMessage());
$this->error('Failed: ' . $e->getMessage());
}
}
}

