【CakePHP3】ログイン認証を使って管理画面を作る

CakePHP3CakePHP3


Warning: Undefined variable $content in /home/c9099599/public_html/dstrikes.net/wp-content/themes/cocoon-child-master/tmp/content.php on line 76
この記事は 約12 分で読めます。

ここ3日間でCakePHP3がかなり理解出来てきました。
やっぱり手を動かして書きながらテストしないと覚えませんね。
書く事によって実際どう言う動作をするかが初めて理解出来ると思います。
そして公式サイトの言う通りにソースをコピペしても実際には動かない場合もあります。(苦笑
さて、それでは実際にログイン認証をテストしてみたいと思います。

参考:シンプルな認証と認可のアプリケーション

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(255),
    role VARCHAR(20),
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

まずはユーザーテーブルを生成します。

公式によると、取りあえずbakeしちゃって良いみたいなのでbakeしちゃいます。

bin\cake bake all users

username と password のカラムをユーザテーブルに使用すると、CakePHPはユーザログインの実装のときにほとんどのことを自動で定義します。
と公式マニュアルが言ってますのでUsersテーブルでusername passwordと言うカラムを作っておくとお得な事があるようですね。

src/model/table/usersTable.phpの編集

src/model/table/usersTable.php を開いてマニュアルにあるようにしてみます。
取りあえずInitialのFunctionは不要だったようなので削除しました。
更にValidateFunctionを以下のように書き換えます。

<?php
namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

    public function validationDefault(Validator $validator)
    {
        return $validator
            ->notEmpty('username', 'A username is required')
            ->notEmpty('password', 'A password is required')
            ->notEmpty('role', 'A role is required')
            ->add('role', 'inList', [
                'rule' => ['inList', ['admin', 'author']],
                'message' => 'Please enter a valid role'
            ]);
    }

}

ついでにfunction buildRules(RulesChecker $rules)なんてものもbakeされてたんだけど、こっちはコメントアウトで対応してみる。

src/Controller/UsersController.phpの編集

<?php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class UsersController extends AppController
{
    // Other methods..

    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
        // Allow users to register and logout.
        // You should not add the "login" action to allow list. Doing so would
        // cause problems with normal functioning of AuthComponent.
        $this->Auth->allow(['add', 'logout']);
    }

    public function login()
    {
        if ($this->request->is('post')) {
            $user = $this->Auth->identify();
            if ($user) {
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('Invalid username or password, try again'));
        }
    }

    public function logout()
    {
        return $this->redirect($this->Auth->logout());
    }
}

ここで重要なのは $this->Auth->allow([‘add’, ‘logout’]);これかな?
要するに add と logout のページだけは認証が無くてもページ閲覧を許可すると言う指定らしい。
このbeforFilterAppControllerに書く事で全てのページを制御する事も出来るらしい。

上記リンクの公式マニュアルのページを全てコピペする事で実装する事は出来たのだけども、結局は自分が理解しないと自由に記述する事が出来ないのでここからは自分が理解した事を解説したいと思う。

まずココの記事が分かり易かったので参考にします。
http://jmatsuzaki.com/archives/16505

public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Flash'); // Flashコンポーネント。エラーメッセージの表示などに使用
        $this->loadComponent('RequestHandler'); // RequestHandlerコンポーネント。入力されたデータの取得などに使用
        $this->loadComponent('Auth', [ // Authコンポーネントの読み込み
            'authenticate' => [
                'Form' => [ // 認証の種類を指定。Form,Basic,Digestが使える。デフォルトはForm
                    'fields' => [ // ユーザー名とパスワードに使うカラムの指定。省略した場合はusernameとpasswordになる
                        'username' => 'mail', // ユーザー名のカラムを指定
                        'password' => 'pass' //パスワードに使うカラムを指定
                    ]
                ]
            ],
            'loginRedirect' => [ // ログイン後に遷移するアクションを指定
                'controller' => 'Tasks',
                'action' => 'index'
            ],
            'logoutRedirect' => [ // ログアウト後に遷移するアクションを指定
                'controller' => 'Users',
                'action' => 'login',
            ],
            'authError' => 'ログインできませんでした。ログインしてください。', // ログインに失敗したときのFlashメッセージを指定(省略可)
        ]);
    }

もう読んだらそのままですよね。
公式では $this->loadComponent(‘RequestHandler’);この辺の記載はないのですがAppControllerに記載する場合は必要なようです。
上記の記載はUsersControllerAppControllerに記載する事によってログインしないとアクセス出来ないページとして扱えます。

1 ログイン実装の手順 For CakePHP3

ログインコンポーネント(イニシャライズ=初期化)

public function initialize()
	{
		parent::initialize();
		$this->loadComponent('Flash');
		//認証
		$this->loadComponent('Auth',[
		'authenticate' => [
			'Form' => [
				'fields' => [
					'username' => 'username',
					'password' => 'password'
				]
			]
		],
		'loginAction' => [
			'controller' => 'Users',
			'action' => 'login'
		]
	]);
	}

Table名Controller.php か AppController.php のイニシャライズに設置。
Table名に書くとそのテーブルに関するページ全てを指定したことになり
AppControllerに書くとサイト全体に対してログインが必要となります。
aucenticateは認証する値が username password のコンビであれば設定不要です。
それ以外は Form => からの連想配列で指定してあげればそれを元に認証してくれます。
loginActionについてはログイン後の遷移先を記載します。

2 ログインアクション Login()

public function login()
	{
		if($this->request->is('post')){
			$user = $this->Auth->identify();
			if($user){
				$this->Auth->setUser($user);
				return $this->redirect($this->Auth->redirectUrl());
			}
			$this->Flash->error('ユーザー名かパスワードが間違ってまっせ');
		}
	}

ログインさせたいコントローラーからLoginを実装する。
失敗した場合は画面遷移させないでそのままエラーを表示
ログインに成功したらリダイレクトで手順1で設定したURLへ遷移します。

$user = $this->Auth->identify();の部分でログイン情報をDBから探して情報がマッチしていれば$userにその情報が代入されます。

3 アクセス許可設定

このページはログインしてなくてもアクセスしてイイヨと言うページを作ります。

public function beforeFilter(Event $event){
        parent::initialize();
        $this->Auth->allow(['add']);
    }

そんなページのコントローラー部分でBeforeFilterを設定してやれば良いです。
$this->Auth->allow([‘add’,’view’])などカンマ区切りでガンガン追記していけます。

4 ログイン後の挙動

ログイン後は特にログインした時のデータを明示的に保存しなくてもAuth君が自動的に保存してくれています。
$this->set(‘user‘,$this->Auth->user());とコントローラーから呼んであげれば$userにその情報が保持されているのがわかります。
またRoleに権限情報を保存しているのでAdminなのかAutherなのかを明示的にしてあげれば権限管理なども行えます。

5 まとめ

あと、困ったのが必要最小限のソースしか書いていないのにログイン認証されない事がありました。
これは未だに原因は分かって居ないのですがModel/Entity でパスワードハッシャーをしないとログインが通らないようです。
プレーンなDBの情報からではAuthは動作しないと思った方が良さそうですね。

また、あらかじめ作っていたユーザー(ハッシュ済)情報でも認証はされません。
これはSecuritySaltの情報が違うからですね。
かならず、そのCakePHPで作ったHash情報でDBにレコード(保存)して認証をテストしないとダメです。

PHPフレームワーク CakePHP 3入門

掌田 津耶乃 秀和システム 2017-01-14
売り上げランキング : 188707

by ヨメレバ