LoginSignup
15
8

More than 5 years have passed since last update.

[Laravel 5.7] フォーム認証処理の仕組み

Last updated at Posted at 2018-10-26

普通のログインフォームで認証する場合のLaravelの処理を追ってみます。

Laravelのフォーム認証を構成するもの

php artisan make:authした状態です。

  • コントローラ (App\Http\Controllers\Auth\LoginController)
    継承元のloginメソッドでガードを呼び出します。
  • ガード (Illuminate\Auth\SessionGuard)
    ログイン処理を実行します。その中でプロバイダーを呼び出します。
  • プロバイダー (Illuminate\Auth\EloquentUserProvider)
    入力されたIDのユーザー情報をDBから持ってきたり、その情報と入力されたパスワードを比較したりします。
    DB検索にEloquentモデルを使ってクエリーを走らせます。
  • モデル (App\User)
    プロバイダーがDB検索時に使うEloquentモデル。

ガード

Illuminate\Auth\SessionGuardのattemptメソッドがログイン処理を行っています。

Illuminate\Auth\SessionGuard
/**
 * ログインを試みる
 */
public function attempt(array $credentials = [], $remember = false)
{
    // プロバイダのretrieveByCredentials()でDBからユーザー情報を取得しています。
    // $credentialsは、ログインフォームから送った値で['email' => 'foo@example.com', 'password' => 'secret']のような配列です。
    $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

    // hasValidCredentials()の中で、プロバイダのvalidateCredentials()が実行され、$credentialsと上記で取得した情報を比較しています。
    // 比較がtrueであれば
    if ($this->hasValidCredentials($user, $credentials)) {
        // ログイン成功処理(セッションにユーザー情報をセットしたり)します。
        $this->login($user, $remember);
        return true;
    }
    return false;
}

/**
 * リクエストされた値とDBのユーザー情報を比較する
 */
protected function hasValidCredentials($user, $credentials)
{
    return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
}

プロバイダー

ガードで呼ばれたretrieveByCredentials()とvalidateCredentials()を見てみます。

Illuminate\Auth\EloquentUserProvider
/**
 * リクエストされた情報でDBからユーザー情報を取得する
 */
public function retrieveByCredentials(array $credentials)
{
    // $credentialsが空かpasswordしか無ければreturn
    if (empty($credentials) ||
        (count($credentials) === 1 &&
        array_key_exists('password', $credentials))) {
        return;
    }

    // パスワードを除いた値でクエリーを生成しています。
    // ['email' => 'foo@example.com', 'password' => 'secret']であれば、emailだけ使用されます。
    $query = $this->createModel()->newQuery();
    foreach ($credentials as $key => $value) {
        if (Str::contains($key, 'password')) {
            continue;
        }
        if (is_array($value) || $value instanceof Arrayable) {
            $query->whereIn($key, $value);
        } else {
            $query->where($key, $value);
        }
    }
    // クエリーを実行し該当するユーザーを取得します。
    return $query->first();
}

/**
 * リクエストされたパスワードとDBから取得したパスワードを比較する
 */
public function validateCredentials(UserContract $user, array $credentials)
{
    $plain = $credentials['password'];

    return $this->hasher->check($plain, $user->getAuthPassword());
}

モデル

App\Userが継承しているIlluminate\Foundation\Auth\Userは、

  • Illuminate\Database\Eloquent\Model
  • Illuminate\Contracts\Auth\Authenticatableインターフェース

を継承しています。使用されているIlluminate\Auth\Authenticatableトレイトを見てみると、プロバイダーのvalidateCredentials()で使われたgetAuthPassword()などが実装されています。

プロバイダーに使われるモデルは、Illuminate\Contracts\Auth\Authenticatableインターフェースを継承していなければならないということです。

終わりに

例えば、usersテーブルではなくadministratorsテーブルを認証に使いたい管理画面を実装する際は、

  • Authenticatableインターフェースを実装したAdministratorモデルを作る
  • config/auth.phpで設定し、authミドルウェア、ルーティングも設定する

以上を行えば良いわけです。プロバイダー、ガードはEloquentモデルとsessionを利用する限り同じように使えます。
ちなみにモデルを用意せずDBにアクセスできるDatabaseUserProviderもあります。

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8