当ブログ人気記事!
転職体験談
Laravelで学ぶWebアプリ開発
誰でも作れるチャットアプリ
未経験への勧め

【Laravelライブラリ解説】ユーザー登録の処理を読み解いてみた

プログラミング

「Laravelのユーザー認証機能簡単に実装できるけど、どういう処理が走っているのかよく分からない…」

今回の記事はこんな悩みに対して書きました。

本記事を書いているSHOOT(@Shoot58153748)

未経験からWeb系に転職したWebエンジニア1年目で、
主にLaravelでアプリ開発しています。


会社では独自のユーザー認証方法を用いるものの、

Laravel標準のユーザー認証ってありますよね?

Laravelを使い始めた当初仕組みがさっぱりでした。

どういう処理が行われているのか知らないと気持ち悪いですよね??


そこで本記事では
標準ユーザー認証の処理の流れを解説します!

ぜひ参考にしてください!

スポンサーリンク

ユーザー認証(Register)のだいたいの流れ


  1. 登録画面を表示する

  2. ユーザー登録画面からPOSTフォームでユーザー情報(name,email,password)を送信する

  3. ユーザー情報をバリデーションチェックする

  4. ユーザー情報をDBに保存する

  5. ログイン(ユーザーIDをSession保存)する

  6. Home画面にリダイレクトする

ユーザー認証準備

// ユーザー認証に必要なルーティングを追加する
php artisan make:auth

// ルーティング一覧を確認する
php artisan route:list


実際の登録処理(POST)は
RegisterControllerのregisterに
ルーティングされています。

登録処理詳細

app/Http/Auth/RegisterController.php

<?php

namespace App\Http\Controllers\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}

処理の順番としては以下になります。

1. ミドルウェア(コンストラクター)
2. ハンドラー(ルーティングで指示されたメソッド)


コンストラクターはクラスが生成時に必ず実行させる処理です。

public function __construct()
     {
         $this->middleware('guest');
     }


‘guest’の定義に関しては
app/Http/Kernel.phpに記述されています。

app/Http/Kernel.php

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];


‘guest’というエイリアスが登録されており、
\App\Http\Middleware\RedirectIfAuthenticated::classを実行。

app/Http/Middleware/RedirectIfAuthenticated.php

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/home');
        }

        return $next($request);
    }

handleメソッド内を実行します。
ログイン(ユーザー情報のセッションが残っていたら)していたら
ホーム画面にリダイレクト。

Auth::guard(ファサード)に関しては後述します。


今回は未ログインという前提で話を進めます。

回答画面からユーザーネーム、メールアドレス、パスワードを送信(POSTリクエスト)されると、
RegisterControllerのregisterメソッドにルーティングされます。

ですが、、、regisiterメソッドが見当たりません。

実は、
use RegistersUsers;
の中に処理が隠れていました。

useは、宣言したクラスを使うことができます。

/vendor/laravel/framework/src/Illuminate/foundation/Auth/RegistersUsers.php

    /**
     * Show the application registration form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showRegistrationForm()
    {
        return view('auth.register');
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }

    /**
     * Get the guard to be used during registration.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }

registerというメソッドがありました!

この中に、登録処理の全てが記述されています。

以下の流れです。
ユーザー情報をバリデートする(validotor)
↓↓
DBに登録(create)
↓↓
セッションに保存(guard()->login())
↓↓
リダイレクト(return)

validator, createメソッドはControllerに定義されてました。


$this->guard()は同クラス内に定義されており、

Auth::guard()というAuthファサードのguardメソッドを返しています。



ちなみに、以下のイベントクラスでDB登録したユーザーを設定します。

event(new Registered($user = $this->create($request->all())));

/vendor/laravel/framework/src/Illuminate/events/Registered.php

    /**
     * The authenticated user.
     *
     * @var \Illuminate\Contracts\Auth\Authenticatable
     */
    public $user;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @return void
     */
    public function __construct($user)
    {
        $this->user = $user;
    }
}


さて、ログイン処理を行なっているAuthファサードクラスを見てみましょう

/vendor/laravel/framework/src/Illuminate/support/facades/Auth.php

/**
 * @method static mixed guard(string|null $name = null)
 * @method static void shouldUse(string $name);
 * @method static bool check()
 * @method static bool guest()
 * @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
 * @method static int|null id()
 * @method static bool validate(array $credentials = [])
 * @method static void setUser(\Illuminate\Contracts\Auth\Authenticatable $user)
 * @method static bool attempt(array $credentials = [], bool $remember = false)
 * @method static bool once(array $credentials = [])
 * @method static void login(\Illuminate\Contracts\Auth\Authenticatable $user, bool $remember = false)
 * @method static \Illuminate\Contracts\Auth\Authenticatable loginUsingId(mixed $id, bool $remember = false)
 * @method static bool onceUsingId(mixed $id)
 * @method static bool viaRemember()
 * @method static void logout()
 * @method static \Symfony\Component\HttpFoundation\Response|null onceBasic(string $field = 'email',array $extraConditions = [])
 * @method static null|bool logoutOtherDevices(string $password, string $attribute = 'password')
 * @method static \Illuminate\Contracts\Auth\UserProvider|null createUserProvider(string $provider = null)
 * @method static \Illuminate\Auth\AuthManager extend(string $driver, \Closure $callback)
 * @method static \Illuminate\Auth\AuthManager provider(string $name, \Closure $callback)
 *
 * @see \Illuminate\Auth\AuthManager
 * @see \Illuminate\Contracts\Auth\Factory
 * @see \Illuminate\Contracts\Auth\Guard
 * @see \Illuminate\Contracts\Auth\StatefulGuard
 */
class Auth extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'auth';
    }

    /**
     * Register the typical authentication routes for an application.
     *
     * @param  array  $options
     * @return void
     */
    public static function routes(array $options = [])
    {
        static::$app->make('router')->auth($options);
    }
}


「あれ、メソッドどこに書かれているんだ?」

と思うかもしれませんが、継承元のFacade.phpに
getFacadeAccessorで’auth’を返すことで複数のクラス

@see \Illuminate\Auth\AuthManager
@see \Illuminate\Contracts\Auth\Factory
@see \Illuminate\Contracts\Auth\Guard
@see \Illuminate\Contracts\Auth\StatefulGuard


を参照する仕組みになっています。

Facadeの処理に関しては別記事で詳しく流れを追いたいと思います。

以下が、Authファサードのguardメソッドです。

/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php

    /**
     * Attempt to get the guard from the local cache.
     *
     * @param  string|null  $name
     * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
     */
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
    }

    /**
     * Resolve the given guard.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
     *
     * @throws \InvalidArgumentException
     */
    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 driver [{$config['driver']}] for guard [{$name}] is not defined."
        );
    }

    /**
     * Create a session based authentication guard.
     *
     * @param  string  $name
     * @param  array  $config
     * @return \Illuminate\Auth\SessionGuard
     */
    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;
    }

    /**
     * Get the guard configuration.
     *
     * @param  string  $name
     * @return array
     */
    protected function getConfig($name)
    {
        return $this->app['config']["auth.guards.{$name}"];
    }

    /**
     * Get the default authentication driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['auth.defaults.guard'];
    }



guardの中身を読み解きましょう。

$nameに

“web”

が代入されます。
これは、getDefaultDriver()内で

config/auth.phpのdefaults.guardの値を参照しています。

その次に、resolveメソッドに”web”を渡します。

$config[‘driver’]は”session”なので、
$driverMethodにcreateSessionDriverが代入され、実行。

参考までにconfigファイルです。

  • config/auth.php
    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],


createSessionDriverメソッドの中で、

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

SessionGuardのインスタンスを作成して$guardに代入しています。


そこで、SessionGuardの中身をみてみると、

ついにloginメソッドがあります!



以下がSessionGuardのloginメソッドです。

/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php

    /**
     * Log a user into the application.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  bool  $remember
     * @return void
     */
    public function login(AuthenticatableContract $user, $remember = false)
    {
        $this->updateSession($user->getAuthIdentifier());

        // If the user should be permanently "remembered" by the application we will
        // queue a permanent cookie that contains the encrypted copy of the user
        // identifier. We will then decrypt this later to retrieve the users.
        if ($remember) {
            $this->ensureRememberTokenIsSet($user);

            $this->queueRecallerCookie($user);
        }

        // If we have an event dispatcher instance set we will fire an event so that
        // any listeners will hook into the authentication events and run actions
        // based on the login and logout events fired from the guard instances.
        $this->fireLoginEvent($user, $remember);

        $this->setUser($user);
    }

    /**
     * Update the session with the given ID.
     *
     * @param  string  $id
     * @return void
     */
    protected function updateSession($id)
    {
        $this->session->put($this->getName(), $id);

        $this->session->migrate(true);
    }

セッションを更新(updateSession)します。

ログインイベントと認証済みイベントを実行して、

ログイン処理が終了になります。

最後に、設定したRedirectPathにリダイレクトして登録処理完了です。


ライブラリを読みこむのは勉強になる


本記事では、ユーザー登録処理の流れを解説しましたが、

いかかでしたか?理解が進んだでしょうか?

初心者にとってLaravelはとても便利です。

ただ便利さの反面、フレームワーク独自のコーディングルールやブラックボックスの部分が多いです。

使い始めた当初、

「なぜこういう動作をしているんだろう??」

気持ち悪さもありましたが、
最近標準のライブラリを読むことでLaravelを昔より深く理解できていると感じます。


綺麗なソースコードを読めますし、Laravelの動作原理も学べるので

ライブラリを読み込むのおすすめです。


それではまた!

プログラミング
スポンサーリンク
シェアする
SHOOTをフォローする
WebエンジニアSHOOTのブログ

コメント

タイトルとURLをコピーしました