普通のログインフォームで認証する場合の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メソッドがログイン処理を行っています。
/**
* ログインを試みる
*/
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()を見てみます。
/**
* リクエストされた情報で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もあります。