Securityコンポーネントを利用するコントローラに、jQuery のAjax関数「$.post()」でアクションを実行しようとしても、しっかりとブラックホール行きとなり期待する処理ができない。どうするか、以下は自分で試してみての解決方法です。
先ず、セキュリティを回避してアクションを実行するには、以下のように「beforeFilter()」でSecurityのプロパティに値を設定すればいいようだ。
public function beforeFilter() { parent::beforeFilter(); if ($this->params['action'] == アクション名) { $this->Security->csrfCheck = false; $this->Security->validatePost = false; } }
しかしこのままの利用はセキュリティ上好ましくないので、Securityコンポーネントが出力するワンタイムトークンの利用を考えてみた。その方法は、ビュー内に出力されたトークンをjQueryで拾い上げ、サーバー側にPOSTデータが渡されたとき、セッションに保存されているトークンと比較する方法である。
以下はJavaScriptでトークンを拾い、Ajaxで送信する例。
var params = { 'data[Comment]' : $("input[name='data[Comment]']").val(), 'data[Token][key]' : $("input[name='data[_Token][key]']").val() } $.post('/comments/add', params, function(data) { alert(data); });
Ajaxで送られたデータを受け取って処理する側「/comments/add」では、beforeFilter()でセキュリティを無効にしてadd()でトークンを確認すればよい。トークンはセッションデータに保存されているので、次のようにして読み出す。
$this->token = Session.read('_Token.key');
注意しなければならないのは、add()で読み出してもトークンは既に新しく書き直されているため、決して送ったトークンと一致する事はないことだ。そこで書き直される前、「beforeFilter()」で必ず読み出しておく必要がある。add()アクションでは次のように比較して確認する。
if ($this->request->data['Token']['key'] == $this->token) :
さてここで困った事に。それは、1回ならこのAjax通信は処理できる。が、続けての通信はセッションデータ内のトークンが書き直されているため、エラーになってしまうのである。
これを回避するための考え付いた方法が、Ajax通信する前の最初のアクションでセッションにトークンを保存しておき、比較はCakePHPが保存したトークンを使わず、自分で保存したトークンを使うのである。
これで取り敢えず解決した。
が、またまた問題である。
ブラウザで同じ画面を複数開くと、自分で保存したトークンの値が書き直されてしまうため、新しく開いた画面では処理可能だが、前の画面ではエラーになるのである。
皆さんならどのように解決するでしょうか?
自分の考え付いた方法はトークンの値として、Security.saltとセッションIDのハッシュ値を利用する方法です。これならログイン中は、複数画面で同じAjax処理が可能になるのではないかと。