マルチ認証と言っても、複数のステップでユーザーを認証するわけでもなく、ちょっとピンと来ないですね。

例えばECシステムにおいて、ユーザー画面での会員ログインと、管理画面での管理者のログインがそれぞれ別に必要とします。どちらもログインはEメールとは限らないし、片方でログインしたらもう片方でも認証となるとも限りません。つまり、ログインするユーザーの種類や場所が複数必要となる状況が多々あります。それに対応する機能が、マルチ認証です。

Laravelの5.1までは、マルチ認証は対応していなく、以下のようなパッケージをインストールして使用していました。

Laravel4.2対応のLaravel Multi Auth

Laravel5.1対応のMultiAuth for Laravel 5.1

しかし、5.2からはLaravelの基本仕様となっています。さすが、Taylorくん!

今回はこの機能を見てみましょう。

まず、デフォルトでインストールされるconfig/auth.phpの中身の解析。

Laravel 5.1では、

return [

    /* デフォルトの認証ドライバー */

    'driver' => 'eloquent',

    /* 認証に使用されるモデル */

    'model' => App\User::class,

    /* 認証に使用されるDBテーブル。ここでは、dirverがdatabaseでないので関係ない */

    'table' => 'users',

    /* パスワードリセットの設定 */
    'password' => [
        'email'  => 'emails.password', // resources/views/emails/passwordをリンク送信メールのテンプレートとする
        'table'  => 'password_resets',  // パスワードリセットのトークンの情報を保存するDBテーブル
        'expire' => 60,                 // トークンは60分で期限切れ
    ],
];

これがLaravel5.2では、

return [
    /* 認証のデフォルト設定 */

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

    /* 認証のガードを定義 */                                                   

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

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

    /* 認証のプロバイダー */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

   /* パスワードリセットの設定 */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'email' => 'auth.emails.password',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];

となりました。違いは、guardsprovidersの導入です。

ちょっとこれではわかりにくいので、先の例を使って、ECサイトを想像してもらって、ショッピングをするユーザ画面と、サイトを管理する管理者画面があり、どちらもログインで認証が必要と仮定しましょう。ユーザー画面では買い物かごをチェックアウトするには、会員のログインが必要とします。

となると必要な設定は以下にようになります。

return [
    /* 認証のデフォルト設定 */

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

    /* 認証のガードを定義 */                                                   

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

        'admin_users' => [
            'driver' => 'session',
            'provider' => 'admin_users_provider',
        ],
    ],

    /* 認証のプロバイダー */

    'providers' => [
        'users_provider' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

        'admin_users_provider' => [
            'driver' => 'eloquent',
            'model' => App\AdminUser::class,
        ],
    ],

   /* パスワードリセットの設定 */

    'passwords' => [
        'users' => [
            'provider' => 'users_provider',
            'email' => 'auth.emails.password',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];

ガードには、会員ログインのためのusersと、管理者ログインのためのadmin_usersの2つを定義します。どちらもセッションを使って、ログイン後の画面をプロテクトします。また、それらのプロバイダーで定義されているように、会員のUsersと管理者のAdminUsersのエロクエントモデルが認証のための情報提供元となります。

guardsprovidersの概念を導入することにより、今までの1つだけの認証のメカニズムを複数としたわけです。

さて、次はこの設定の使用です。このファイル以外で使用するのは、ガード名だけですから簡単です。

ガードを指定する場所はプログラムの中でいくつかありますが、以下のようにapp/Http/routes.phpで使用されるのが一番明確と思います。

例えば、ユーザー画面では、

    Route::group(['middleware' => 'guest:users'], function() {
        Route::get('login', 'user\AuthController@getLogin');
        Route::post('login', 'user\AuthController@postLogin');
        Route::get('signup', 'user\SignupController@getSignup');
        Route::post('signup', 'user\SignupController@postSignup');
        Route::get('password/email', 'user\PasswordController@getEmail');
        Route::post('password/email', 'user\PasswordController@postEmail');
        Route::get('password/reset/{token}', 'user\PasswordController@getReset');
        Route::post('password/reset', 'user\PasswordController@postReset');
    });

    Route:: group(['prefix' => 'member', 'middleware' => 'auth:users'], function() {
        Route::get('index', 'user\MemberController@getIndex');
        Route::get('password', 'user\MemberController@getPassword');
        Route::post('password', 'user\MemberController@postPassword');
        Route::get('profile', 'user\MemberController@getProfile');
        Route::post('profile', 'user\MemberController@postProfile');
        Route::get('logout', 'user\AuthController@getLogout');
    });

以前、ユーザー認証(4)認証でページを保護で説明したように、

ミドルウェアとして、guestauthが使われます。しかし、前回と違って、guest:usersのようにガード名を指定することが必要です。指定がないなら、auth.phpのデフォルトのセクションで指定したガードが自動的に使われます。

ちなみに、管理者側では、

Route::group(['prefix' => 'admin', 'middleware' => 'guest:admin_users'], function()
{
    Route::get('login', 'admin\AuthController@getLogin');
    Route::post('login', 'admin\AuthController@postLogin');
});

Route:: group(['prefix' => 'admin', 'middleware' => 'auth:admin_users'], function() {

    Route::get('logout', 'admin\AuthController@getLogout');
    Route::get('index', 'admin\HomeController@getIndex')->name('admin.home');
..

こんな感じです。

AuthControllerは、ユーザ画面と管理画面では、前回紹介した自動作成使われるもののコピーを編集する必用あります。

ユーザ画面では、

...

class AuthController extends BaseController
{
    protected $guard = 'users';

    protected $redirectTo = 'user/member/index';   // ログイン後のリダイレクト先
    protected $redirectAfterLogout = 'user/login';   // ログアウト後のリダイレクト先

    protected $username = 'email';               // ログインとなるDBの項目名

    protected $maxLoginAttempts = 5;             // ログインスロットルとなるまで最高のログイン失敗回数
    protected $lockoutTime = 60;                 // ログインスロットルとなってからの待ち秒数

    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    public function showLoginForm()
    {
        return view('user.login');  //テンプレートの場所を変える
    }
..

管理画面では、

...

class AuthController extends BaseController
{
    protected $guard = 'admin_users';

    protected $redirectTo = 'admin/index';   // ログイン後のリダイレクト先
    protected $redirectAfterLogout = 'admin/login';   // ログアウト後のリダイレクト先

    protected $username = 'login';               // ログインとなるDBの項目名

    protected $maxLoginAttempts = 5;             // ログインスロットルとなるまで最高のログイン失敗回数
    protected $lockoutTime = 60;                 // ログインスロットルとなってからの待ち秒数

    use AuthenticatesUsers, ThrottlesLogins;

    public function showLoginForm()
    {
        $form = new \stdClass();

        $form->login = Form::text('login', '',
            ['size' => 20, 'maxlength' => 20, 'class' => 'en', 'autofocus' => 'autofocus']);

        $form->password = Form::password('password',
            ['size' => 40, 'maxlength' => 20, 'class' => 'en']);

        return view('admin.login')->with(compact('form')); //テンプレートの場所を変える
    }
..

となります。認証が2つとなると、テンプレートなどいろいろな指定が必要となることに注意してください。上の例では、会員のログインは、emailですが、管理者のログインは、emailでなくてもよい文字列という仮定です。

最後に、ログイン後にログインしたユーザーの情報がほしいときは、今まで、

Auth::user()

でしたが、マルチ認証となると、

Auth::guard('users')->user()
Auth::guard('admin_users')->user()

と明確にガード名を指定する必用があります。

By khino