CakePHP4でログイン、ログアウトを実装する

CakePHP4でログイン、ログアウトの処理を実装してみたいと思います。

CakePHP3までは、AuthComponentで実装していましたが、CakePHP4からAuthComponentが非推奨となりました。
▼公式ドキュメント
https://book.cakephp.org/4/ja/controllers/components/authentication.html

CakePHP4では「authentication」プラグインを使用して、実装します。

authenticationプラグインのインストール

以下のコマンドを実行して、「authentication」プラグインをインストールします。

composer require "cakephp/authentication:^2.0"

パスワードを暗号化する

「src/Model/Entity/User.php」に以下の処理を追加します。

protected function _setPassword($value)
{
    $hasher = new DefaultPasswordHasher();
    return $hasher->hash($value);
}

上記の記述方法をミューテーターと呼び、passwordというプロパティを設定する際は上記の処理が必ず呼ばれるようになります。
中でパスワードを暗号化(ハッシュ化)して、ハッシュ化した文字列を返す、という処理になります。

ログイン処理の実装

src/Application.phpの修正

「src/Application.php」を修正します。
まずは使用するクラスのインポートを追加します。

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;

続いて、認証のインターフェースを実装します。

class Application extends BaseApplication implements AuthenticationServiceProviderInterface

続いて、既存のmiddlewareメソッドに認証のミドルウェアを追加します。
この時、「RoutingMiddleware」より後ろになるように追加する必要があります。

->add(new RoutingMiddleware($this))

// add Authentication
->add(new AuthenticationMiddleware($this))

続いて、認証ロジックのメソッドを実装します。

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;
}

4行目で、未ログイン時にログイン画面にリダイレクトするように指定します。
11、12、20,21行目で、それぞれ認証情報に使用するメールアドレスとパスワードのプロパティを指定します。

src/Controller/AppController.phpの修正

続いて、「src/Controller/AppController.php」を修正します。
既存のinitializeメソッドに、認証ロジックを読み込むための以下の記述を追加します。

$this->loadComponent('Authentication.Authentication');

src/Controller/UsersController.phpの修正

続いて、「src/Controller/UsersController.php」を修正します。
まず、ログイン画面は認証不要で表示するようにアクション名を指定します。

public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['login']);
}

続いて、ログインメソッドを追加します。

public function login()
{
    $result = $this->Authentication->getResult();
    if ($result->isValid()) {
        // ログインしていればログイン後の画面にリダイレクト
        $target = $this->Authentication->getLoginRedirect() ?? '/mypage';
        return $this->redirect($target);
    }
    // ログイン認証に失敗した場合はエラーを表示する
    if ($this->request->is('post') && !$result->isValid()) {
        $this->Flash->error(__('メールアドレスまたはパスワードが誤っています。'));
    }
}

4行目でログイン状態かどうかチェックします。
ログイン画面を開いた際にログインしていた場合、および、ログイン画面でメールアドレスとパスワードを入力して認証し、ログイン成功した場合は、ログイン後の画面にリダイレクトします。
これはGETでもPOSTでもログイン認証が完了していればリダイレクトするため、HTTPメソッドの判定が入っていません。

6行目の「$this->Authentication->getLoginRedirect()」という記述は、ログイン後にリダイレクトする画面を取得しています。
未ログイン状態で認証が必要な画面にアクセスした場合、ログイン画面にリダイレクトします。
この時、元々アクセスした画面が取得できるので、ログイン後に元の画面に戻れるという仕組みです。

「?? '/mypage’」という記述は、上記のログイン後にリダイレクトする画面が指定されていなかった場合に表示する画面を指定しています。

10行目はログイン認証に成功したかどうかのチェックです。
チェックするのはPOST時のみのため、POSTかどうかの判定をしています。

ログアウト処理の実装

「UsersController.php」に以下の処理を追加します。
ログアウト処理を行ってから、ログイン画面にリダイレクトする処理になります。

public function logout()
{
    $result = $this->Authentication->getResult();
    if ($result->isValid()) {
        $this->Authentication->logout();
    }
    return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}