ユーザー認証(10)Laravel 5.2 マルチ認証では、会員と管理者に対して異なるDBテーブルをもとに認証を設定しました。

また、

前回では、違うHasherの使用を試みました。

今回は、マルチ認証のときに異なるHasherを用いるケースについて考えてみましょう。そうたくさん起こるケースでないかもしれませんが、私のクライアントのシステムでは実際に起こるケースです。1つのシステムにおいて、「会員」と「管理者」と「店舗管理者」が存在し、それぞれの認証は異なるHasherを使用しています。特に「管理者」は複数のシステムで共有するもので、その認証のためのサーバーが違うマシンに存在します。

まず直面する問題は、Laravelの5.2のマルチ認証では、このような状況にはシンプルに対応できないことです。

config/auth.phpprovidersでは、それぞれのproviderにおいてhasherの設定がないのです。

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

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

また、前回のように、グローバルでHasherを変えることもできません。

「会員」の認証のときには、デフォルトのHasherを使用し、「管理者」の認証のときには、違うHasherを使用できるのが理想です。

まず、Authのサービスがどう初期化されているか追跡してみましょう。

AuthServiceProviderで、ユーザー認証のサービスauthが登録されます。そこでは、AuthManagerのクラスが使用されます。

  protected function registerAuthenticator()
    {
        $this->app->singleton('auth', function ($app) {
            $app['auth.loaded'] = true;
            return new AuthManager($app);
        });
        $this->app->singleton('auth.driver', function ($app) {
            return $app['auth']->guard();
        });
    }

AuthManagerでは、config/auth.phpの設定を読み込み、providerを作成します。その作成は、CreatesUserProvidersで行われます。

..
    public function createSessionDriver($name, $config)
    {
        $provider = $this->createUserProvider($config['provider']);
..

providerdriverは、先のconfig/auth.phpではeloquentと設定されているので、以下のメソッドでオブジェクトが作成されます。

..
   protected function createEloquentProvider($config)
    {
        return new EloquentUserProvider($this->app['hash'], $config['model']);
    }
..

やっとたどり着きましたね。そうproviderの作成時に、グローバルのHasher app['hash']がパラメとして渡されているのです。

ここを変えることができれば、認証のHasherを変えることできるのです。変更するには、新規の認証のためのdriverを使用することも可能です。しかし、以下のEloquentUserProviderを見ると、オブジェクトが作成された後でもHasherを変えることが可能のようです。

...
    /**
     * Sets the hasher implementation.
     *
     * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher
     * @return $this
     */
    public function setHasher(HasherContract $hasher)
    {
        $this->hasher = $hasher;
        return $this;
    }
...

ここまで理解すると、あとはそう難しくはありません。

まずは、config/auth.phpにおいて、どのHasherのサービスを使用するか指定しましょう。

...
   'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
       'hasher' => Illuminate\Hashing\BcryptHasher::class
        ],

        'admin_users' => [
            'driver' => 'eloquent',
            'model' => App\AdminUser::class,
            'hasher' => App\Services\MD5Hasher::class
        ],
    ],
...

上のhasherはまったくLaravelのコードでは使用されませんが、今回の目的でHasherを指定するにはベストの場所と思いませんか!

次に、管理者のAuthControllerにおいて、

...
class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins {
        getCredentials as getCredentialsTrait;
    }

    protected $redirectTo = '/admin/home';

    protected $guard = 'admin';
    protected $redirectAfterLogout = 'admin/login'; //ログイン後のリダイレクト先
    protected $username = 'email'; // DBテーブルのログインに使用される項目

    protected $registerView = 'admin.auth.register'; // 登録に使用されるテンプレート
    protected $loginView = 'admin.auth.login'; // ログインに使用されるテンプレート

    protected $hasher;

    public function __construct()
    {
        $hasher_class = config('auth.providers.admin_users.hasher');

        $this->hasher = new $hasher_class;

        \Auth::guard($this->guard)->getProvider()->setHasher($this->hasher);

        $this->middleware('guest:admin', ['except' => 'logout']);
    }
...

会員と違うテンプレートを用意するために、テンプレートの場所を指定していることにも注意してください。すべて変数の指定で可能です。

PasswordControllerにおいても同様な設定をします。

class PasswordController extends Controller
{
    use ResetsPasswords;

    protected $redirectTo = '/admin/home'; // ログイン後のリダイレクト先
    protected $guard = 'admin';

    protected $linkRequestView = 'admin.auth.passwords.email'; // パスワードのリセットリンクを送信してもらう画面のテンプレート
    protected $resetView = 'admin.auth.passwords.reset'; // パスワードリセット画面のテンプレート
    protected $subject = '管理者のパスワードリセット'; // 送信メールの件名

    public function __construct()
    {
        $hasher_class = config('auth.providers.admin_users.hasher');

        \Auth::guard($this->guard)->getProvider()->setHasher(new $hasher_class);

        $this->middleware('guest:admin');
    }
}

最後に、管理者へのパスワードリセットのメールのテンプレートは、以下のように、config/auth.phpで可能です。

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

        'admin_users' => [
            'provider' => 'admin_users',
            'email' => 'admin.auth.emails.password',
            'table' => 'admin_password_resets',
            'expire' => 60,
        ],
    ],
];

By khino