タイトルの通り、同時ログインを禁止したいときの処理について。
コードは会社にあるので概要だけだが、簡単な内容なので困らないと思う。
知らないうちに第三者に勝手にログインされているのはセキュリティ上も怖いので、2重ログインが必要なアプリでなければ実装しておきたい。
いつも頼りにしているスタッコオーバーフローであまり良いスレッドが見つけられず、結局普通に自分で作った。
##基本方針
- 後でログインしたセッションを有効とし、過去のセッションは全て強制ログアウトする
- sessionはDB保存している場合を想定
- ログイン時、DBから過去の同一ユーザーのsessionsを削除する(=強制ログアウト)
sessionsテーブルにカラム追加
CakePHP標準のsessionテーブルにはid ,dataカラムがあるが、idはCookieのセッションID(ランダムな文字列)、dataはシリアライズされたセッションデータになっている。
すなわち、どのユーザーのデータか単純に調べられず、過去セッションの削除が簡単にできないため、追加カラム user_idを持たせる。
もし複数アプリでDB共用する場合、applicationカラムも必要。
user_id INT,
application vachar(255), #複数アプリでDB共用する場合のみ必要
###カスタムSession Handlerを作成&設定
sessionsテーブルのuser_idに自動で書き込むため、カスタムハンドラを作成。
Cookbookのカスタムセッションハンドラーの作成をベースにすれば簡単に作れる。(自分で書くのは数行くらい)
必要なのはwrite()のみで、ここで継承元のDatabaseSessionを見つつ、
user_idを書き込むように処理を追加する。
またapp.php等に設定も追加する。
なお、良いデータ取り回しが今一わからないので、Controllerからuser_idを伝えるのには
Configure::write(), read()で行ったが、終了時(?) Configureのデータがほぼ空になるタイミングでもwrite()が呼ばれるようなので、データがあるか否か確認してから書き込みを行う必要がある。
(何も考えずにConfigure::read()を放り込むと、nullで上書きされてしまう)
※Behaviorでもできると思うが、意味的にハンドラの方が良いと思う
###コントローラからuser_idをSession Handlerに伝える
ログイン処理(&認証不要処理)アクション、またAppController - isAuthorized()あたりでuser_idを伝えるようにする。
良い方法が思いつかず、Configure::write()/read()でやった。
Singletonかstaticでの情報受け渡し用クラスを作るのが正しいか?
追記
AuthComponentを呼べないという意味ではModelでも同じなので、その線でStackOverflowやgithub検索してみたものの、やはりConfigureかstaticでの受け渡し用クラス作る位しか見つけられなかった。
###ログイン時の過去セッション削除
ログイン処理時、そのuser idの過去セッションをsessionsテーブルから全削除する。
これにより、過去セッションは全てログアウトされる。
※ログアウト処理($this->Auth->logout())でもsessionテーブルのデータを消している
※このタイミングでは今回ログインのデータはまだ書き込まれていないようだが、バージョンによるかもしれない。今回ログインも消える場合は最新以外を消すよう調整。
$user = $this->Auth->identify();
if($user){
ログイン成功時の処理...
(追加)
Sessionsテーブルから user_id = $user['id'] のデータを全削除 (deleteAll)
}