Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Laravelのmiddleware authを理解したい (5.5)

More than 1 year has passed since last update.

6/21 13:22 最終更新完了です。

Laravelの認証の仕組みを利用することは、公式ドキュメントや色々な記事を読むことで可能になりましたが、
「じゃあ実際どういう仕組みで認証してるんだろう」という疑問にぶち当たり、調べて見ました。

Laravelのmiddleware authを理解したい
こちらの記事のLaravel5.5版です。構成も丸パクリさせていただいています。

環境

Laravel 5.5
PHP 7.0
MacOSX 10.13

調査

php artisan make:auth
で自動生成されたコードによれば、認証が必要なものはMiddlewareで定義する。

class HomeController extends Controller
{
    //コンストラクタ
    public function __construct()
    {
        $this->middleware('auth');
    }

・Http/Kernel.phpにて「auth」というaliasを登録している

Http/Kernel.php
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,

※ 5.5〜 Framework内のクラスを利用するように変更された(らしい)

\Illuminate\Auth\Middleware\Authenticate でやっていること

  • handleが定義されている(自明ですかね
  • ガードを指定することで、認証の方式を変更できる
    • $guards に指定された文字列が入る($this->middleware('auth:web,api'); のように複数指定が可能なので配列)
  • $guardsが指定されていないときは、\Illuminate\Auth\AuthManager\authenticate() を呼ぶ → 本資料はこちらについてだけ理解を進める
    • 実装は interface Illuminate\Contracts\Auth\Factory だが、実際にDIコンテナに登録されているのは上のクラス(\Illuminate\Foundation\Application\registerCoreContainerAliases())

\Illuminate\Auth\AuthManager\authenticate() が呼ばれるまで

  • \Illuminate\Auth\AuthManager\authenticate() はコード上存在しないです。
\Illuminate\Auth\AuthManager.php
    public function __call($method, $parameters)
    {
        return $this->guard()->{$method}(...$parameters);
    }

$this->guard() でオブジェクトが特定されるまで

Illuminate\Auth\AuthManager.php
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
    }
  • $this->resolve() で解決している模様
  • $nameは、"web"(デフォルト: auth.default.web)
Illuminate\Auth\AuthManager.php
    protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($name, $config);
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';

        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

        throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
    }
  • $config['driver'] は、config/auth.php で定義しているdriverが読み込まれる。
    • デフォルトのままだと'session'
  • $driverMethod = 'createSessionDriver' で話を進める
Illuminate\Auth\AuthManager.php
    public function createSessionDriver($name, $config)
    {
        $provider = $this->createUserProvider($config['provider'] ?? null);

        $guard = new SessionGuard($name, $provider, $this->app['session.store']);

        // When using the remember me functionality of the authentication services we
        // will need to be set the encryption instance of the guard, which allows
        // secure, encrypted cookie values to get generated for those cookies.
        if (method_exists($guard, 'setCookieJar')) {
            $guard->setCookieJar($this->app['cookie']);
        }

        if (method_exists($guard, 'setDispatcher')) {
            $guard->setDispatcher($this->app['events']);
        }

        if (method_exists($guard, 'setRequest')) {
            $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
        }

        return $guard;
    }
  • SessionGuard が、guardクラス

createUserProviderでやっていること

↓ 5.2解説より (同じ模様)

  • configのauth.providers.{$provider}の値 → auth.providers.usersの値を取得する
    • デフォルト:auth.providers.users = ['driver'=>'eloquent','model'=>'App\User']
  • 「何を使って、どこからUser情報を取得するか」というのを取得する。
  • デフォルトでは、「eloquentを使って、App\Userモデルの情報をとる」ということ

本稿の追記

  • Laravelで用意されているUserProviderは以下
    • auth.providers.users.driver = 'eloquent'の場合は、Illuminate\Auth\EloquentUserProvider
    • 'database'の場合は、Illuminate\Auth\DatabaseUserProvider
  • UserProviderの主なメソッドは以下
    • retrieveById():IDで認証する
    • retrieveByToken():token(remember me)で認証する
    • updateRememberToken():token(remember me)を更新する
    • retrieveByCredentials():credentialを更新する
    • validateCredentials():credentialを利用してユーザを検証する

new SessionGuard()でやっていること

Illuminate\Auth\SessionGuard.php
    public function __construct($name,
                                UserProvider $provider,
                                Session $session,
                                Request $request = null)
    {
        $this->name = $name;
        $this->session = $session;
        $this->request = $request;
        $this->provider = $provider;
    }

SessionGuard::authenticate() でやっていること

  • authenticate() メソッドは Illuminate\Auth\GuardHelper に存在(use で読み込まれている)
GuardHelper.php
    public function authenticate()
    {
        if (! is_null($user = $this->user())) {
            return $user;
        }

        throw new AuthenticationException;
    }

user() がやっていること

  • GuardHelperの$this->userは、SessionGuard に実装されている(戻った)
  • $this->loggedOut = true だったらnullを返す
    • logout() で trueがセットされる。ログアウトしたけどSessionが残っているのを防ぐ模様
  • $this->user が存在していれば、それを返す
    • DBアクセスを減らす目的
    • これに該当することってあるんですかね?
  • SessionからIDを取得
    • Sessionについては別記事でまとめる
        $id = $this->session->get($this->getName());
  • IDが取れたら、UserProviderのretriveByIdで、Userオブジェクトを取得する
    • 取得できたら認証完了のイベント通知を行う
  • $recaller = $this->recaller() で、Recallerオブジェクト生成
    • "remember me" Cookieの情報を保持する
    • 情報がない場合はnull
  • ここまでで$this->user がとれていなくて、$recaller が存在している場合、Recallerから$this->userの取得を試みる
  • 最後にログインユーザが取れていれば$this->user 、取れていなければnull(ログインなし)を返す

以上です。

darum
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away