51
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-05-25

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(ログインなし)を返す

以上です。

51
51
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
51
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?