CakePHP4でクッキーを使用して、自動ログインを実装しようと思ったところ、CakePHP3.5でクッキーの仕様が大きく変わっていましたので、紹介します。
CakePHP3.4まではCookieComponentを使用していましたが、CakePHP3.5から非推奨になっていました。
公式ドキュメントには以下の記述があるのですが、これがよく分からなかったので、紐解いてみます。
クッキーは、 ServerRequest で利用できます。 クッキー をご覧ください。 クッキーの暗号化は クッキー暗号化ミドルウェア をご覧ください。
https://book.cakephp.org/4/ja/controllers/components/cookie.html
本記事ではCakePHP4.1.5を使用しています。
自動ログイン機能の流れ
ログイン時に「ログインしたままにする」というチェックボックスにチェックを付けて、ログインすると、一定期間はずっとログインしたままにして、ログイン画面を開いても自動でログインするようにする、ということを実現してみます。
ログイン時に「ログインしたままにする」というチェックボックスにチェックを付けて、ログインした場合に、「自動ログインをする」という情報をクッキーに保存しておきます。
ログイン画面を開いた際にクッキーが保存されていれば、自動でログインを行う、という仕組みです。
ログイン時にクッキーを保存する
ログイン時にクッキーを保存する処理は以下のようになります。
use Cake\Http\Cookie\Cookie;
use DateTime;
public function login()
{
$result = $this->Authentication->getResult();
if ($result->isValid()) {
if ($this->request->is('post') && $this->request->getData('autologin') === '1') {
// ログイン情報をDBとクッキーに保存する
$user = $result->getData();
$db_user = $this->Users->get($user['id']);
$set_key = hash('sha256', (uniqid() . mt_rand(1, 999999999) . '_auto_logins'));
$db_user->login_key = $set_key;
$this->Users->save($db_user);
$this->response = $this->response->withCookie(Cookie::create(
User::KEY_AUTO_LOGIN,
$set_key,
[
'expires' => new DateTime('+1 month'),
]
));
}
// ログイン済みの場合はリダイレクト
$target = $this->Authentication->getLoginRedirect() ?? '/mypage';
return $this->redirect($target);
}
....
}
5行目で「ログインしたままにする」のチェックボックスにチェックが入っているかを判定します。
チェックが入っている場合は、9行目でユニークなIDを生成し、sha256で暗号化してログインキーとします。
10、11行目でログインキーをDBに保存しておきます。
13行目でログインキーをクッキーに保存します。
14行目がクッキーの名前になります。本記事では User::KEY_AUTO_LOGIN とUserエンティティに定数を用意しましたが、「AUTO_LOGIN」という名前でクッキーを保存します。
15行目が保存するログインキーです。
16行目からはオプションで、本記事ではクッキーの有効期限を指定しています。
「+1 month」としているので、1ヶ月後がクッキーの有効期限となります。
クッキーが保存されると、以下のように確認できると思います。
(Chromeで確認した例です)
ログイン画面を開いた際に、クッキーを取得する
クッキーを取得して自動ログインする処理は以下のようになります。
public function login()
{
......
if ($this->request->is('get')) {
// クッキーにログインキーが保存されていて、DBと一致する場合、自動ログインする
$autoLoginKey = $this->request->getCookie(User::KEY_AUTO_LOGIN);
if ($autoLoginKey != null) {
$user = $this->Users->find()->where(['login_key' => $autoLoginKey])->first();
if ($user != null) {
$this->Authentication->setIdentity($user);
return $this->redirect(['controller' => 'Mypage']);
}
}
}
}
getで画面を開いた際にクッキーを取得します。
6行目がクッキーを取得する処理です。
保存時と同様に、名前は定数で指定しています。
7行目で判定を入れて、クッキーが取得できていれば、8行目で対象のログインキーを条件にDBから対象ユーザーを検索します。
ログインキーが一致するユーザーがいれば、10行目で自動ログイン処理を行って、11行目でログイン後の画面にリダイレクトしています。
ログアウト時にクッキーを削除する
ログアウトする場合は、自動ログインの情報を全て削除します。
ログアウト時にクッキーを削除する処理は以下のようになります。
public function logout()
{
$result = $this->Authentication->getResult();
if ($result->isValid()) {
$user = $this->request->getAttribute('authentication')->getIdentity();
$db_user = $this->Users->get($user['id']);
if ($db_user->login_key != null) {
$db_user->login_key = null;
$this->Users->save($db_user);
$this->response = $this->response->withExpiredCookie(new Cookie(User::KEY_AUTO_LOGIN));
}
$this->Authentication->logout();
}
$this->Flash->success('ログアウトしました。');
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}
6行目でログイン中のユーザー情報を元に、DBから対象ユーザーを検索します。
8、9行目でDBのログインキーをnullに更新します。
そして、11行目でクッキーを削除します。
同じ名前でクッキーを生成し、有効期限のない(すぐに無効になる)状態で保存し直すことで消える仕組みのようです。
▼公式ドキュメント
https://book.cakephp.org/4/ja/controllers/request-response.html#response-cookies
https://book.cakephp.org/4/ja/controllers/request-response.html#request-cookies