前回と前々回は CakePHP2 から CakePHP4 の移行作業として、CakePHP2 側のソースのリファクタリングと、CakePHP4 の仕様に合わせてソースの一括変換を行うコンバータの考え方について書きました。今回は認証機能の移行について書きたいと思います。
認証機能の追加
CakePHP4 の認証には、CakePHP2 と同様に AuthComponent を使用する方法と、Authentication プラグインを使用する方法の2つの方法があります。これまでと同様、AuthComponent を使用したいところですが、CakePHP4 では非推奨となっており、今後 Authentication プラグインに置き換えられる予定となっています。そこで今後も長く使用できるように Authentication プラグインを使用することといたしました。
具体的には以下のような手順で認証機能を追加します。
1. composer を使って Authentication プラグインをインストールします。
(事前に composer のインストールが必要です。コマンドプロンプトでルートディレクトリに移動し、以下のコマンドを実行します。)
composer require "cakephp/authentication:^2.0"
2. src\Application.php に以下のクラスを追加します。
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Identifier\IdentifierInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;
3. src\Application.php のクラスの宣言を以下のとおり変更します。
// 変更前
class Application extends BaseApplication
// 変更後
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
4. src\Application.php に以下のメソッドを追加します。
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
->add(new RoutingMiddleware($this))
->add(new AuthenticationMiddleware($this));
return $middlewareQueue;
}
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
$authenticationService = new AuthenticationService([
'unauthenticatedRedirect' => '/users/login',
'queryParam' => 'redirect',
]);
$authenticationService->loadAuthenticator('Authentication.Session');
$fields = [
IdentifierInterface::CREDENTIAL_USERNAME => 'username',
IdentifierInterface::CREDENTIAL_PASSWORD => 'password'
];
$authenticationService->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => '/users/login',
]);
return $authenticationService;
}
5. src\Controller\AppController.php の initialize メソッドに以下のコードを追加します。
$this->loadComponent('Authentication.Authentication');
6. 無限リダイレクトループを防止するために、src\Controller\UsersController.php と src\Controller\Admin\UsersController.php beforeFilter のメソッドに以下のコードを追加します。
$this->Authentication->addUnauthenticatedActions(['login']);
7. src\Controller\UsersController.php と src\Controller\Admin\UsersController.に以下のメソッドを追加(変更)します。
public function login()
{
$this->request->allowMethod(['get', 'post']);
$result = $this->Authentication->getResult();
// 認証がOKの場合
if($result->isValid())
{
// ログイン後の画面に遷移
return $this->redirect(['controller' => 'Users', 'action' => 'index']);
}
// ユーザーが submit 後、認証失敗した場合は、エラーを表示します
if($this->request->is('post') && !$result->isValid())
{
$this->Flash->error(__('ログインID、もしくはパスワードが正しくありません'));
}
}
より詳しい情報は以下のURLにて確認可能です。
CakePHP Authentication 2.x Cookbook
https://book.cakephp.org/authentication/2/ja/index.html
CakePHP2 で保存されたパスワードの引き継ぎ
CakePHP4 ではパスワードのハッシュ化のアルゴリズムに bcrypt が採用されています。CakePHP4 で新規にシステムを構築する場合はこの方法で問題ないのですが、CakePHP2 からの移行の場合、既存のユーザのパスワードのハッシュを引き継ぐ必要があります。CakePHP2 ではハッシュ関数に SHA-1 が使用されており、スムーズな移行が可能なようにCakePHP4 でも SHA-1 をサポートすることにしました。
具体的には以下の手順で SHA-1 のハッシュの設定を行います。
- config\app_local.php にCakePHP2側の app\Config\core.php で指定していた Security.salt を以下のように指定します。
'Security' => [
'salt' => env('SECURITY_SALT', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'),
],
2. 指定した Security.salt を使用するため config\bootstrap.php に以下のコードを追加します。
Security::setSalt(Configure::read('Security.salt'));
3. src/Application.php の getAuthenticationService メソッドに以下のコードを追加します。
$service->loadIdentifier('Authentication.Password', [
'passwordHasher' => [
'className' => 'Authentication.Fallback',
'hashers' => [
'Authentication.Default',
[
'className' => 'Authentication.Legacy',
'hashType' => 'sha1',
'salt' => true
],
]
]
]);
これで CakePHP2 で保存したパスワードを使用して、CakePHP4 でもログインができるようになります。
bcrypt でハッシュ化されたパスワードについて
CakePHP2 で使用していたパスワードを引き継ぎ、CakePHP4 でも同じパスワードでログインできるようになりましたが、CakePHP4 でパスワードを変更した場合は、bcrypt にて以下のような形式でハッシュ化され、データベースに保存されます。
$2y$10$AbCd1EfGH2IjkL3MN4fGH2IjkL3jkL3MN4fGH2IjkL3MN4fGH2Ijk
これは次のような意味があります。
1-3桁目 | $2y | ハッシュアルゴリズム(2, 2a, 2b, 2x, 2y 等の種類があります。) |
4-6桁目 | $10 | ストレッチング回数(2のストレッチング回数分、連続してハッシュ化を行います。10の場合、2の10乗=1024回ハッシュ値への計算を行います。) |
7-29桁目 | AbCd1EfGH2IjkL3MN4fGH2I | ソルト(パスワードに付加する文字列。CakePHP2 では設定ファイルに記述していましたが、bcrypt ではレコードごとに変化します。) |
30-59桁目 | jkL3jkL3MN4fGH2IjkL3MN4fGH2Ijk | ハッシュ値(ストレッチング回数分計算した結果のハッシュ値。入力されたパスワードのハッシュ値と、保存されているハッシュ値が一致していれば認証がOKとなります。) |
CakePHP2 は全てのパスワードでソルトが固定となっていましたが、CakePHP4 ではレコードごとに異なり、CakePHP2 に比べてパスワードの安全性が向上しています。
非常に参考になります。
実際行われてみた時の、他のノウハウなども期待しています。