LaravelでログインのThrottle機能をカスタマイズする

こんにちは、コバヤシです。 今回は、Laravel(6.x)でログインのThrottle機能をカスタマイズしてみました。

Throttle機能とは

Throttle機能とは、ログイン失敗時に失敗した回数を超えた場合にログインロックを掛けて、一定の時間を経過したらロックを解除する機能です。

Throttle機能を使う

使い方は簡単です。 $maxAttemptsと$decayMinutesをLoginController内に設定するだけです。

<?php

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $maxAttempts = 5; // 5回失敗したらロックする
    protected $decayMinutes = 30; // ロックは30分間

ロックした時にメッセージを出すようにする

ここからが本題です。
Laravelでは$maxAttemptsに設定された回数失敗したらロックされるのですが、ロックされた旨のメッセージは設定した回数失敗した際には表示されません。 どういうことかと言うと、例えば5回の制限をかけていた場合、ロックしたメッセージが出るのは5回失敗したときではなくて6回目にログインをした時なのです。
ロックしたらその時に知らせてほしいもの。。。。 ということで、ロックしたメッセージがすぐに表示されるようにしてみます。

ロックのエラー処理は、loginメソッド内でやっています。 ログイン判定前に、処理をしているようなので、ログイン判定後に改めてロックの判定を行うように追記します。

<?php
    public function login(Request $request)
    {
        $this->validateLogin($request);
  
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }
  
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }
  
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);
  
       // このif文を追記
        if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }
  
        return $this->sendFailedLoginResponse($request);
    }

同じ判定を2回書いているので冗長ですが、、良しとしておきます。
これでロックしたらすぐにメッセージが出るようになります。

失敗回数はログインID毎ではなく全体での回数にする

Laravelではログインの失敗のカウントは、ログインID毎に判別しているようです。
つまり、あるアカウントはロックされても、別のアカウントならログイン可能ということです。 これを全体で失敗した回数にしたいと思います。

throttleKeyというメソッドでログインを判別するためのキーを生成しているので、これをLoginController内でオーバーライドして修正します。

Str::lower($request->input($this->username())).'|'.$request->ip()となっていて、ログイン名(通常はemail)とIPを元にキーを生成しています。 このキーを元にカウントアップするので、アカウントごとに失敗した回数が設定されるというわけですね。

なので、ここを以下のように修正します。

    protected function throttleKey(Request $request)
    {
        return $request->ip();
    }

これでアカウントは関係なく同一IPからの失敗でカウントされるようになります。

まとめ

Laravelではデフォルトでは仕様に沿わないなぁと言う箇所でも比較的簡単に修正して対応できることが多いですね。
またTipsがあったらブログに書いていきたいと思います。