前回からの続き
やはり認証処理は試しておかないという事で、前回作ったアプリにログインの仕組みを追加していきます。
ということで、前回、Userコントローラじゃなくて、AddressBookコントローラとかにしとけば良かったなぁと後悔していますが、とりあえず、前回は前回で、折角作ったメール機能ガン無視でログインの仕組みを追加していきます。
認証プラグインの追加
てっきり初めから入っていると思いましたが、認証機能はプラグインを追加でインストールするようです。
Dockerのコンテナから以下のコマンドを実行します。
> docker exec -it php_svr /bin/bash
/var/www/html# cd testapp
/var/www/html/testapp# composer require cakephp/authentication
ちなみに日本語訳のドキュメントは記述が古く、ここでauthentication:2.0とバージョンを指定してしまうとエラーになります。
というか???と思い英語のドキュメントを確認したら全然違いますね。
なんだか若干シンプルになってます。
テストプログラムの改造
テーブルにpasswordフィールド追加
やり方はいろいろあるとは思いますが、今回もDB関係は雑な説明で、最終的に以下のテーブルとなるようにテーブル変更してください。
CREATE TABLE users
(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(32),
email VARCHAR(255),
+ password VARCHAR(255),
PRIMARY KEY (id)
);
Application に認証に仕組み追加
ドキュメントに従い、ただただ無心にコードを追加していきます。
ファイル名:src/Application.php
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Identifier\AbstractIdentifier;
use Authentication\Identifier\IdentifierInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
public function bootstrap(): void
{
parent::bootstrap();
+ $this->addPlugin('Authentication');
:
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
:
->add(new BodyParserMiddleware())
+ // Add the AuthenticationMiddleware. It should be
+ // after routing and body parser.
+ >add(new AuthenticationMiddleware($this))
:
/**
* Returns a service provider instance.
*
* @param \Psr\Http\Message\ServerRequestInterface $request Request
* @return \Authentication\AuthenticationServiceInterface
*/
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
// Define where users should be redirected to when they are not authenticated
$service->setConfig([
'unauthenticatedRedirect' => [
'prefix' => false,
'plugin' => false,
'controller' => 'Users',
'action' => 'login',
],
'queryParam' => 'redirect',
]);
// Define identifiers
$fields = [
AbstractIdentifier::CREDENTIAL_USERNAME => 'email',
AbstractIdentifier::CREDENTIAL_PASSWORD => 'password'
];
$passwordIdentifier = [
'Authentication.Password' => [
'fields' => $fields,
],
];
// Load the authenticators. Session should be first.
$service->loadAuthenticator('Authentication.Session', [
'identifier' => $passwordIdentifier,
]);
$service->loadAuthenticator('Authentication.Form', [
'identifier' => $passwordIdentifier,
'fields' => $fields,
'loginUrl' => Router::url([
'prefix' => false,
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
]),
]);
return $service;
}
AppController に認証に仕組み追加
ドキュメントに従い、ただただ無心にコードを追加していきます。
ファイル名:src/Controller/AppController.php
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Flash');
+ $this->loadComponent('Authentication.Authentication');
:
ここで公式のドキュメントでは、viewとindexはログイン無しで表示できるようにと細工してますが、ログインしない子には内容は一切見せないぞと、そんな仕組みは追加しません。
ちなみにこの部分です。
// in a controller beforeFilter or initialize
// Make view and index not require a logged in user.
$this->Authentication->allowUnauthenticated(['view', 'index']);
コントローラにログイン機能追加
ドキュメントに従い、ただただ無心にコードを追加していきます。
ファイル名:src/Controller/UsersController.php
public function login()
{
$result = $this->Authentication->getResult();
// If the user is logged in send them away.
if ($result && $result->isValid()) {
$target = $this->Authentication->getLoginRedirect() ?? '/index';
return $this->redirect($target);
}
if ($this->request->is('post')) {
$this->Flash->error('Invalid username or password');
}
}
public function logout()
{
$this->Authentication->logout();
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}
とは言え、これではログインしないと何も表示できなくなるので、loginとaddは見えるようにします。
public function beforeFilter(\Cake\Event\EventInterface $event)
{
parent::beforeFilter($event);
+ $this->Authentication->allowUnauthenticated(['login', 'add']);
}
公式ドキュメントではloginのみ許可してますが、パスワード、たぶんハッシュ化してるのに、ユーザ追加ページ使えないとテストできないよって感じです。
あくまでテスト用なので、addも許可しときます。
あとはビューを作ります。
ドキュメントに従い、ただただ...(いい加減しつこい)
ファイル名:templates/Users/login.php
<!-- in /templates/Users/login.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>
ファイル名:templates/Users/add.php
:
<?php
echo $this->Form->control('name');
echo $this->Form->control('email');
+ echo $this->Form->control('password');
?>
:
最後にパスワードのハッシュ化の設定
ユーザーを追加するときに、パスワードはハッシュ化してからDBに保存します。
平文のままパスワード保存するとか、ありえないです。
そんな恐ろしい時代ははるか昔に終わったと信じてます。ん?
+ use Authentication\PasswordHasher\DefaultPasswordHasher;
class User extends Entity
{
:
+ // Automatically hash passwords when they are changed.
+ protected function _setPassword(string $password)
+ {
+ $hasher = new DefaultPasswordHasher();
+ return $hasher->hash($password);
+ }
}
以上で、ログインしてない状態でアクセスするとログインページに飛ばされるようになります。
ログオフはボタン増やしてないので、とりあえず、URL直打ちで
「http://localhost:8080/testapp/users/logout/」
とすればログイン画面に戻ります。
最後に
公式ドキュメントに従って、無心でコードを追加するだけで、簡単に認証機能が追加できました。
最初日本語訳読んで、苦労したのは内緒です。最新バージョン使ってるときは、最初から英語版読むべきですね。