ここでは、Authentication プラグインを利用したユーザー認証の実装方法を解説いたします。
詳細については、公式サイトを参照してください。
また、公式サイトのチュートリアルでは、ユーザーIDにメールアドレスを利用していますが、
ここでは、ユーザー名を利用して、認証する方法で解説いたします。
公式サイト
ユーザーに関連するコード作成
テーブルの作成
はじめに、ユーザーデータを保持するためのデータベースの中に新しいテーブルを作成します。
モデル修正
ログイン画面の生成
まずは、bakeでひな形を作成し、その後に、model、controllerを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
[ec2-user@ip-xxx-xxx-xxx-xxx html]$ bin/cake bake all users Bake All ------------------------------------------------------------------------------- Warning Error: /var/www/html/tmp/cache/models/ is not writable In [/var/www/html/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php, line 427] 2022-03-29 02:45:39 warning: Warning (512): /var/www/html/tmp/cache/models/ is not writable in [/var/www/html/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php, line 427] Trace: Cake\Error\BaseErrorHandler::handleError() - CORE/src/Error/BaseErrorHandler.php, line 185 Cake\Cache\Engine\FileEngine::_active() - CORE/src/Cache/Engine/FileEngine.php, line 427 Cake\Cache\Engine\FileEngine::init() - CORE/src/Cache/Engine/FileEngine.php, line 101 Cake\Cache\CacheRegistry::_create() - CORE/src/Cache/CacheRegistry.php, line 88 Cake\Core\ObjectRegistry::load() - CORE/src/Core/ObjectRegistry.php, line 110 Cake\Cache\Cache::_buildEngine() - CORE/src/Cache/Cache.php, line 157 Cake\Cache\Cache::pool() - CORE/src/Cache/Cache.php, line 232 Cake\Database\Connection::getCacher() - CORE/src/Database/Connection.php, line 837 Cake\Database\Connection::getSchemaCollection() - CORE/src/Database/Connection.php, line 397 Bake\Command\ModelCommand::execute() - ROOT/vendor/cakephp/bake/src/Command/ModelCommand.php, line 87 Bake\Command\AllCommand::execute() - ROOT/vendor/cakephp/bake/src/Command/AllCommand.php, line 115 Cake\Console\BaseCommand::run() - CORE/src/Console/BaseCommand.php, line 179 Cake\Console\CommandRunner::runCommand() - CORE/src/Console/CommandRunner.php, line 334 Cake\Console\CommandRunner::run() - CORE/src/Console/CommandRunner.php, line 172 [main] - ROOT/bin/cake.php, line 12 One moment while associations are detected. Baking table class for Users... Creating file /var/www/html/src/Model/Table/UsersTable.php Wrote `/var/www/html/src/Model/Table/UsersTable.php` Deleted `/var/www/html/src/Model/Table/.gitkeep` Baking entity class for User... Creating file /var/www/html/src/Model/Entity/User.php Wrote `/var/www/html/src/Model/Entity/User.php` Deleted `/var/www/html/src/Model/Entity/.gitkeep` Baking test fixture for Users... Creating file /var/www/html/tests/Fixture/UsersFixture.php Wrote `/var/www/html/tests/Fixture/UsersFixture.php` Deleted `/var/www/html/tests/Fixture/.gitkeep` Bake is detecting possible fixtures... Baking test case for App\Model\Table\UsersTable ... Creating file /var/www/html/tests/TestCase/Model/Table/UsersTableTest.php Wrote `/var/www/html/tests/TestCase/Model/Table/UsersTableTest.php` Done Baking controller class for Users... Creating file /var/www/html/src/Controller/UsersController.php Wrote `/var/www/html/src/Controller/UsersController.php` Bake is detecting possible fixtures... Baking test case for App\Controller\UsersController ... Creating file /var/www/html/tests/TestCase/Controller/UsersControllerTest.php Wrote `/var/www/html/tests/TestCase/Controller/UsersControllerTest.php` Done Baking `index` view template file... Creating file /var/www/html/templates/Users/index.php Wrote `/var/www/html/templates/Users/index.php` Baking `view` view template file... Creating file /var/www/html/templates/Users/view.php Wrote `/var/www/html/templates/Users/view.php` Baking `add` view template file... Creating file /var/www/html/templates/Users/add.php Wrote `/var/www/html/templates/Users/add.php` Baking `edit` view template file... Creating file /var/www/html/templates/Users/edit.php Wrote `/var/www/html/templates/Users/edit.php` Bake All complete. |
UsersTable の修正
src/Model/Table/UsersTable.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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' ]); } } |
UsersController
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
namespace App\Controller; use App\Controller\AppController; use Cake\Event\EventInterface; class UsersController extends AppController { public function index() { $this->set('users', $this->Users->find('all')); } public function view($id) { $user = $this->Users->get($id); $this->set(compact('user')); } public function add() { $user = $this->Users->newEmptyEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { $this->Flash->success(__('The user has been saved.')); return $this->redirect(['action' => 'add']); } $this->Flash->error(__('Unable to add the user.')); } $this->set('user', $user); } } |
add.php
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div class="users form"> <?= $this->Form->create($user) ?> <fieldset> <legend><?= __('Add User') ?></legend> <?= $this->Form->control('username') ?> <?= $this->Form->control('password') ?> <?= $this->Form->control('role', [ 'options' => ['admin' => 'Admin', 'author' => 'Author'] ]) ?> </fieldset> <?= $this->Form->button(__('Submit')); ?> <?= $this->Form->end() ?> </div> |
認証の追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[ec2-user@ip-xxx-xxx-xxx-xxx html]$ composer require "cakephp/authentication:^2.0" ./composer.json has been updated Running composer update cakephp/authentication Loading composer repositories with package information Info from https://repo.packagist.org: #StandWithUkraine Updating dependencies Lock file operations: 1 install, 0 updates, 0 removals - Locking cakephp/authentication (2.9.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Downloading cakephp/authentication (2.9.0) - Installing cakephp/authentication (2.9.0): Extracting archive 2 package suggestions were added by new dependencies, use `composer suggest` to see details. Generating autoload files 55 packages you are using are looking for funding. Use the `composer fund` command to find out more! |
パスワードハッシュの追加
次に User エンティティを作成し、パスワードハッシュを追加します。
src/Model/Entity/User.php エンティティファイルを作成し、以下を追加します。
src/Model/Entity/User.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
namespace App\Model\Entity; use Cake\Auth\DefaultPasswordHasher; use Cake\ORM\Entity; class User extends Entity { // 主キーフィールドである「id」以外のすべてのフィールドを一括代入可能にします。 protected $_accessible = [ '*' => true, 'id' => false ]; // ... protected function _setPassword($password) { if (strlen($password) > 0) { return (new DefaultPasswordHasher)->hash($password); } } // ... } |
これでパスワードのプロパティがユーザに割り当てられるたびに DefaultPasswordHasher クラスを使ってハッシュ化されるようになります。
認証の設定
src/Application.php で、以下のインポートを追加します。
1 2 3 4 5 |
use Authentication\AuthenticationService; use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; use Psr\Http\Message\ServerRequestInterface; |
アプリケーションクラスに認証インターフェースを実装します。
src/Application.php
1 2 3 |
class Application extends BaseApplication implements AuthenticationServiceProviderInterface { |
その後、次のように追加します。
src/Application.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
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' => 'username', 'password' => 'password', ] ]); // 認証子をロードするには、最初にセッションを実行する必要があります $authenticationService->loadAuthenticator('Authentication.Session'); // メールとパスワードを選択するためのフォームデータチェックの設定 $authenticationService->loadAuthenticator('Authentication.Form', [ 'fields' => [ 'username' => 'username', 'password' => 'password', ], 'loginUrl' => '/users/login', ]); return $authenticationService; } |
AppController クラスに以下のコードを追加します。
src/Controller/AppController.php
1 2 3 4 5 6 7 8 9 |
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 に以下のコードを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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')); } } |
すべての view と index のページにログインせずにアクセスできるようにする場合には、
以下の設定を AppController に追加します。
src/Controller/AppController.php
1 2 3 4 5 6 7 |
public function beforeFilter(\Cake\Event\EventInterface $event) { parent::beforeFilter($event); // このアプリケーションのすべてのコントローラのために、 // インデックスとビューのアクションを公開し、認証チェックをスキップします $this->Authentication->addUnauthenticatedActions(['index', 'view']); } |
ログアウト
ログアウトアクションを UsersController クラスに追加します。
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 |
public function logout() { $result = $this->Authentication->getResult(); // POSTやGETに関係なく、ユーザーがログインしていればリダイレクトします if ($result->isValid()) { $this->Authentication->logout(); return $this->redirect(['controller' => 'Users', 'action' => 'login']); } } |
これで /users/logout にアクセスしてログアウトすることができます。
そうするとログインページが表示されるはずです。
動作確認
ユーザーの追加
/users/add にアクセスして、ユーザーの登録を行います。
ログイン
/user/ にアクセスして、未認証のため、ログイン画面が表示されます。
ユーザー名、パスワードを入力して、ログインします。
認証が成功すると、ユーザー一覧画面が表示されます。
ログアウト
/users/logout にアクセスすると、再び、ログイン画面が表示されます。