Laravel6.7でマルチログインをできる限り自分好みに実装したはなし


システムユーザを、利用者と管理者で分けたいと思ったので、Laravelの認証の仕組みでそれを実現しないといけなくなりました。
期待通りの動作はしていますが、なんとなく不安なのと、また次あったら嫌なので、覚書きしておくことにしました。

## 認証機能使う上で実現したかったこと
- テーブルの項目を好きにしたかった
- テーブル1個で実現したかった

概ね実現できました。

## 分からなかったこと
1. user,adminとテーブル分けなくても良いか
- どうやって利用者や管理者として認証するのか
- 認証の契機は何か
- ディレクトリ構造とかってなんでもいいのか

大体理解できました。

## やってないこと
- パスワードリセット

ここからは、どのように実現したのかを書いていきます。

## サービスプロバイダとドライバの設定（謎

```PHP:confg/auth.php

// おそらく38行目付近

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

```

driverとproviderをまず設定するんだけど、driverはログインした後のセッションに関連しているのではないかと思われる。
adminsでログインしたらlogin_adminsみたいなセッションの名前に利用されたりするのではないか。
driverはsession以外選択するとどうなるんだろう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26011/152b125a-3874-99ee-4331-099f308a866c.png)

providerはserviceプロバイダと連携ができるのかなって思ってるけど、良く知らない。
分かってるのは、app/providerに定義したファイルが無くても落ちないこと。

## Modelとの関連付け
今度はModelとの関連付け。
EloquentはDB使うときの何かみたいな感じ。
言葉で説明しなくても、みんないつも使ってるだろ？

```PHP:confg/auth.php

// おそらく75行目付近

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

```
テーブル同じやつ使いたいっていうなら、ここのmodelをこのようにしておけば良い。

## Modelの調整
今度は、User.phpを、自分のDBと同じように調整していく。
とりあえず、最初からあるやつは触らないようにしておいた。

```PHP:app/Http/User.php

    use Notifiable;
    protected $table = 'm_auth';  // ここに自分のDBのテーブルの名前
    protected $primaryKey = 'u_id'; // ここに自分のDBのテーブルのプリキーの名前


    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'l_id', 'password', 'l_login', 'join_date'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];


```

テーブル名とかキー名とかはこの記事用にテキトーです。fillableは、レコード取得した時に取得したい項目で、hiddenは要らない項目。
tableとprimaryKeyは自分とこのと同じにできる。ただ、このremenber_tokenだけは何ともならんと思うので、
既存の改修をする場合は、項目追加する必要があるかもしれない。無いかもしれないけどね。わしゃ分からんかった。

## 準備完了。でこれ、どんな契機で使うの？
ここまでやって、自分はこのタイトルのように思いました。
Laravelの最初からあるコード見ると、全部traitん中に書いてあるけど、
postしたデータがtraitん中で勝手に処理されてるようにしか見えない。

悩むのも面倒なので、テストにそれっぽいやつエイヤーする。

したいのはやまやまですが、これをやる前には、テストデータが一つ必要になります。
Laravelの認証使うなら、パスワードはHash::Makeしたものでないとダメです。
一緒に作りましょう。

```PHP:test/Unit/ExampleTest.php


public function testDataTukuruzeeeeee()
    {

        $user = new User();
        $user->******_id = 'hogeyama';
        $user->password = Hash::make('123456');
        $user->***_**** = '2019/12/20 11:38';
        $user->****_**_*** = '2019/12/20 11:38';

        $user->save();

        $user = new User();
        $res = $user->where(['******_id' => 'hogeyama'])->first();
        debug($res);

        $this->assertTrue(true);
    }


```

テストデータができたら、ログインしてみましょう。

```PHP:test/Unit/ExampleTest.php


public function testLoginSuruzeeeee()
    {

        $auth = \Auth::attempt([
            '******_id' => 'hogeyama',
            'password' => '123456'
        ],true);  // true大事


        $user = new User();
        $res = $user->where(['******_id' => 'hogeyama'])->first();
        debug($res);
        
        $this->assertTrue(true);
    }


```

remember_tokenの中に値入っていれば大丈夫だと思われます。
というわけで、Auth::attemptでログインすると良いと思います。
他にも方法はあります。

[Laravel 5.7 認証](https://readouble.com/laravel/5.7/ja/authentication.html)


ちなみに、ここでAuth::attemptをtrueしないと、後のページで認証後のデータ使えないので、
remenber_token項目とtrue必須です。

<blockquote class="twitter-tweet"><a href="https://twitter.com/SEBONEMAN/status/1207940846537953280"/></blockquote>

## あれ、認証は？利用者とか管理者とかは？

Auth::Guard('admins')とやると良いみたいですよ！

```PHP:test/Unit/ExampleTest.php


public function testLoginSuruzeeeee()
    {

        $auth = \Auth::Guard('admins')->attempt([
            '******_id' => 'hogeyama',
            'password' => '123456'
        ],true);


        $user = new User();
        $res = $user->where(['******_id' => 'hogeyama'])->first();
        debug($res);
        
        $this->assertTrue(true);
    }

```

## Auth使うと良いなと思ったこと
- ログイン状態の確認が楽
- 直接ページのURLを叩くすけべぇな輩への手軽な対策ができる

### ログイン状態の確認が楽

constractでmiddlewareで最初にログイン確認できます。

```PHP

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

```
middlewareの中に入る文字は、kernelで確認できます。


```PHP:app/Http/Kernel.php

    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,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];


```



web.phpで使うと、RouteGroup飛んで来たやつに対して対策できます。

```PHP

Route::group(['middleware' => ['guest']], function() {

    Route::get('/abeshi', 'hidebuController@get_hidebu')->name('abesi');

}

```
とすることで、abesiにこようとすると、

```PHP
\App\Http\Middleware\RedirectIfAuthenticated::class
```

こいつが先に動くので、この中でごにょごにょやると快適なスケベ対策になると思います。


## おわり
ハマった割に、そんな書くことないなと思ってたんですが、書いてみると多いもんですね。
なるべくわかりやすくと思ったんですが、ややこしい感じになっちゃうんですね～。