CakePHP3.Xでログイン認証する

## テーブル名、カラム名を変更する
CakePHP公式にあるような「Users」テーブルや、「id」「password」などが使えない場合の手順。

| テーブル名 | M_USER |
| --- | --- |
|カラム名|MAIL_ADDR、PASS、ROLL|

パスワードはハッシュ化（暗号化）したものをDBに持たせること。
その際は`varchar(60)`が最低限必要らしいんですが、[CakePHP公式](https://book.cakephp.org/3.0/ja/tutorials-and-examples/blog-auth-example/auth.html#id2)にならって`varchar(255)`にしました。

~~　`varchar(60)`の根拠はどこにあったかなぁ　~~

暗号化しないままDBに持たせようとすると、[teratailの人たち](https://teratail.com/questions/82048)から厳しく批判されます。

`src\Controller\AppController.php`　に以下のように追記する


```php:src\Controller\AppController.php
    /* ～～中略～～ */
    public function initialize()
    {
        /* ～～中略～～ */
        $this->loadComponent('Security');
        // $this->loadComponent('Csrf');

        $this->loadComponent('Auth', [
            'authorize'      => [ 'Controller'],
            'authenticate'   => [ 'Form' => [ 
                                    'userModel' => 'MUser', // ←ここがミソ
                                    'fields' => ['username' => 'MAIL_ADDR', 'password' => 'PASS' ]  // ←ここがミソ
                                    ] 
                                ],
            'loginRedirect'  => [ 'controller' => 'MUser' , 'action' => 'index' ],
            'logoutRedirect' => [ 'controller' => 'MUser' , 'action' => 'login' ],
            'loginAction'    => '/MUser/login',
            'authError'      => 'ログインしてください'
        ]);
    }
    
    public function isAuthorized($mUser)
    {
        // Admin can access every action
        if (isset($mUser['ROLL']) && $mUser['ROLL'] == 'admin') {
            return true;
        }

        // Default deny
        return false;
    }
```


参考URL：[認証用テーブルを変更する](https://qiita.com/yu1ro/items/5a3c81d2e705475669e9)


## Entityを編集する
ここが一番苦労した

```php:src\Model\Entity\MUser.php
<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
// ﾊﾟｽﾜｰﾄﾞのﾊｯｼｭ化に使用
use Cake\Auth\DefaultPasswordHasher; // ←ここがミソ

    /* ～～中略～～ */
class MUser extends Entity
{
    /* ～～中略～～ */

    // 暗号化処理(関数名は必ず[_setカラム名]とすること)
    protected function _setPASS($value) { // ←ここがミソ
        return (new DefaultPasswordHasher)->hash($value);
    }
}
```
`_set●●●`の部分は、フィールド名のキャメルケースじゃないといけないらしいんですが、フィールド名（カラム）が大文字の場合はどうするのかがわからなかった。

結論は、**全部大文字にする**でOKでした。

あと、コピペ元のソースがちょっと間違っていて、Importが正しくできていなかったり。

## コントローラーを編集する
コントローラー名：`MUserController`

```php:MUserController.php
<?php
namespace App\Controller;

use App\Controller\AppController;
// コントローラーに関連しないテーブルの情報を取得できるようにする
use Cake\ORM\TableRegistry;
// beforeFilterを利用する
use Cake\Event\Event;
/* ～～中略～～ */
class MUserController extends AppController
{
    /* ～～中略～～ */
    public function beforeFilter(Event $event){
        parent::beforeFilter($event);
        
        // ログイン認証不要で使うアクション（デバッグ時のみ有効とする）
        $this->Auth->allow(['add', 'edit', 'delete', 'index', 'logout']); 
    }

    /**
     * role別にアクセスを制御したい場合はここに記述。全ロールに許可する場合はreturn trueとだけ書く
     */
    public function isAuthorized($mUser = null)
    {
        return true;
    }
    
    /**
     * ログインアクション
     */
    public function login()
    {
        if ($this->request->is('post')) {
            $mUser = $this->Auth->identify();
            if ($mUser) {
                $this->Auth->setUser($mUser);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('ユーザーまたはパスワードに誤りがあります。'));
        }
    }

    /**
     * ログアウトアクション
     */
    public function logout()
    {
        // // セッションを破棄
        // $this->request->session()->destroy();
        // まだログインまでしか作ってないので、↑ のソースはコメントアウトしてます
        return $this->redirect($this->Auth->logout());
    }


```

## ログイン画面を作る

`src\Template\MUser\login.ctp`を追加します。
ここはソースの量の少ないし、全部のせちゃう。

```php:src\Template\MUser\login.ctp
<div class="MUser form">
<?= $this->Flash->render() ?>
<?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('メールアドレスとパスワードを入力してください。') ?></legend>
        <?= $this->Form->control('MAIL_ADDR') ?>
        <?= $this->Form->control('PASS') ?>
    </fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>
```

以上。

既存の仕組みをそのままCakePHPに焼き直そうとすると、なかなか大変そうだなぁ。。。