ブログチュートリアル Part.2

CakePHP4のブログチュートリアルを進める。Part.2 book.cakephp.org

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

ユーザーに関連するコードを作成する

ログインユーザーを保持するためにUsersテーブルを作成する。 下記SQLSQLite内で実行。

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

Usersテーブルを作成したので、Bake allする。

bin/cake bake all Users

認証の追加

認証用のプラグインをインストールする。

composer require "cakephp/authentication:^2.0"

パスワードハッシュの追加

パスワードを自動的にハッシュ化するためのメソッドをUserエンティティに追加する。

<?php
protected function _setPassword($password)
{
    if (strlen($password) > 0) {
        return (new DefaultPasswordHasher)->hash($password);
    }
}

認証の設定

認証を有効にするため、下記二つのプラグインをApplication.php内で有効にする。

  • AuthenticationService
  • AuthenticationMiddleware
<?php
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;

アプリケーションクラスに認証インターフェースを実装する。

<?php
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{

インターフェースのメソッドを実装する。

<?php
// src/Application.php
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
    $middlewareQueue
        // ... other middleware added before
        ->add(new RoutingMiddleware($this))
        // add Authentication after RoutingMiddleware
        ->add(new AuthenticationMiddleware($this));

    return $middlewareQueue;
}

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    $authenticationService = new AuthenticationService([
        'unauthenticatedRedirect' => '/users/login',
        'queryParam' => 'redirect',
    ]);

    // 識別子をロードして、電子メールとパスワードのフィールドを確認します
    $authenticationService->loadIdentifier('Authentication.Password', [
        'fields' => [
            'username' => 'email',
            'password' => 'password',
        ]
    ]);

    // 認証子をロードするには、最初にセッションを実行する必要があります
    $authenticationService->loadAuthenticator('Authentication.Session');
    // メールとパスワードを選択するためのフォームデータチェックの設定
    $authenticationService->loadAuthenticator('Authentication.Form', [
        'fields' => [
            'username' => 'email',
            'password' => 'password',
        ],
        'loginUrl' => '/users/login',
    ]);

    return $authenticationService;
}

AppController内で認証用のコンポーネントをロードする。

<?php
// src/Controller/AppController.php
public function initialize(): void
{
    parent::initialize();
    $this->loadComponent('RequestHandler');
    $this->loadComponent('Flash');

    // Add this line to check authentication result and lock your site
    $this->loadComponent('Authentication.Authentication');

UsersController に以下のメソッドを実装する。

<?php
public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);
    // ログインアクションを認証を必要としないように設定することで、
    // 無限リダイレクトループの問題を防ぐことができます
    $this->Authentication->addUnauthenticatedActions(['login']);
}

public function login()
{
    $this->request->allowMethod(['get', 'post']);
    $result = $this->Authentication->getResult();
    // POSTやGETに関係なく、ユーザーがログインしていればリダイレクトします
    if ($result->isValid()) {
        // ログイン成功後に /article にリダイレクトします
        $redirect = $this->request->getQuery('redirect', [
            'controller' => 'Articles',
            'action' => 'index',
        ]);

        return $this->redirect($redirect);
    }
    // ユーザーの送信と認証に失敗した場合にエラーを表示します
    if ($this->request->is('post') && !$result->isValid()) {
        $this->Flash->error(__('Invalid email or password'));
    }
}

ログイン用のtemplate(login.php)を実装する。

<?php
<div class="users form">
    <?= $this->Flash->render() ?>
    <h3>Login</h3>
    <?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('ユーザー名とパスワードを入力してください') ?></legend>
        <?= $this->Form->control('email', ['required' => true]) ?>
        <?= $this->Form->control('password', ['required' => true]) ?>
    </fieldset>
    <?= $this->Form->submit(__('Login')); ?>
    <?= $this->Form->end() ?>

    <?= $this->Html->link("Add User", ['action' => 'add']) ?>
</div>

認証が不要なアクションを定義する。

<?php
// src/Controller/AppController.php で
public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['index', 'view']);
}

ログアウト

ログアウト用のアクションを Usersコントローラーに追加する。

<?php
// src/Controller/UsersController.php で
public function logout()
{
    $result = $this->Authentication->getResult();
    // POSTやGETに関係なく、ユーザーがログインしていればリダイレクトします
    if ($result->isValid()) {
        $this->Authentication->logout();
        return $this->redirect(['controller' => 'Users', 'action' => 'login']);
    }
}

こちらで、 /users/logout にアクセスするとログアウトが完了し、ログイン画面へリダイレクトされるようになる。