記事の対象
-
AuthComponent
によりアクセス制御を行っている - ログインしたら想定してない場所にリダイレクトした
- (特に) リンクでの移動以外でアクセス(ajaxなど)するアクションに,ログインしていないとアクセスが許可されないものがある
本文
AuthComponent::redirectUrl()
について
AuthComponentには redirectUrl()
というメソッドがあり,これは次の優先順位でいずれかのURLを返す.
- 認証情報ストレージ(デフォルトでは
\App\Auth\Storage\SessionStorage
で,セッションに記録される)の redirectUrl() メソッドで取得できるURL-
AuthComponent
によりアクセスを禁止された場合,自動でそのURLがセットされる -
AuthComponent::redirectUrl($url)
にてセット可能
-
-
AuthComponent
の設定で 'loginRedirect' により設定された URL - ホーム
これは次のような遷移を行うのに大変便利である.
- ログインが必要なアクションに,ログインしていないユーザがアクセスした場合に,ログインページへリダイレクトする
- ログインする
- 先ほどアクセスしようとしたページヘリダイレクトする
公式のチュートリアルでも次のように使われている.
// In src/Controller/UsersController.php
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error('Your username or password is incorrect.');
}
}
問題
便利であるが,redirectUrl()
が最優先で返す「アクセスを禁止されたURL」は何もしなければログインするまで(あるいはストレージの記録が消えるまで)残ったままになっている.
そのため,アクセス禁止後にすぐにログインせずに,別のページを経由して,あらためてログインページからログインした際にも,最後にアクセス禁止されたURLを返す.
この結果,チュートリアルの例では,ユーザや開発者が想定していなかったURLにリダイレクトしてしまうという問題が発生しうる.
対策としてこのようにした
AuthComponent::redirectUrl()
は コールすると,セットされている値を失う.
それを利用して次のようにした.
どのアクションからも経由され,各アクションメソッドを実行したあとに実行される箇所,例えばAppController::beforRender()
あたりに,次のようにする.
class AppController extends Controller
{
public function beforeRender()
{
empty($this->protectAuthRedirect) && $this->Auth->redirectUrl();
}
}
そしてログインアクションなどの,アクセスするときにredirectUrlをクリアしたくないアクションでは,コントローラの $protectAuthRedirect
プロパティにtrueをセットすれば良い.
さらに面倒な・・・
ajaxで利用するログインしたユーザだけがアクセスできるアクションを用意している場合,問題がもう少し面倒になる.
セッション期限切れなどでajaxのアクセスに403Forbiddenが返ってくるようになり,ユーザがログインページへ直行した場合,この設定だけだと,ログイン直後に ajaxでアクセスしたURLへリダイレクトことになってしまう.
これはもうloginRedirect()の取得値をフィルターに通すしかない気がする.
(だれかもっとスマートなやり方あれば教えて下さい.)