概要
symfonyはsfWebRequest
とsfWebResponse
オブジェクト経由でCookieへのアクセス方法を提供します。これによってCookieの利用がとても簡単になり、永続的セッションが簡単に実現されます。
ゲッターとセッター
Cookieはクライアントのコンピューターに保存される文字列です。Webアプリケーションによって書き込みされ、同じアプリケーションもしくは同じドメインでのみ読み込みが可能です。
symfonyにおいて、Cookieのためのセッターとゲッターは異なるオブジェクトのメソッドですが、合理的です。Cookieを得るには、sfWebRequest
オブジェクトを使用して、サーバーに送信されたリクエストを調べます。 他方で、Cookieを設定するために、sfWebResponse
オブジェクトを使用して、ユーザーに送信されるリスポンスを修正します。 アクションの範囲内からCookieを操作するには、次のショートカットを使います:
// Cookieのゲッター $string = $this->getRequest()->getCookie('mycookie'); // Cookieのセッター $this->getResponse()->setCookie('mycookie', $value); // オプションつきのゲッター $this->getResponse()->setCookie('mycookie', $value, $expire, $path, $domain, $secure);
setCookie()
メソッドの構文はPHPのsetcookie()
関数のものと同じです(詳しい情報についてはPHPの公式マニュアルを参照してください)。sfWebResponse
メソッドを使う主な利点はsymfonyがCookieのログを記録し、リスポンスが実際に送信されるまで読み込みと修正を続けられることです。
note
アクションの外側からCookieを操作したい場合、ショートカット無しでRequest
とAnswer
オブジェクトにアクセスする必要があります:
$request = sfContext::getInstance()->getRequest(); $response = sfContext::getInstance()->getResponse();
Cookieの使用例: 永続的セッション
(symfonyにおいて完全な透過的である基本的なセッションハンドリングは別として)Cookieの良い用例は永続的セッション機能です。たいていのログインフォームは"remember me"チェックボックスを提供します。それがクリックされると、ユーザーは将来のセッション用にログイン処理を回避することを許可されます。
基本的なログイン機能
security
モジュールを除いてすべてのモジュールが安全であるアプリケーションを想像してみましょう。security/index
アクションへの認証されていないユーザーのリクエストを扱うためにsettings.yml
を設定します:
all: .settings: login_module: security login_action: index
モデルは最低限でもlogin
とpassword
フィールドを持つUser
クラスを持ちます。indexSuccess.php
テンプレートはログインフォームを示し(今のところ"remember me"チェックボックスは無し)、そしてsecurity/login
アクションへの投稿を扱います:
public function executeIndex() { } public function executeLogin() { // ユーザーが存在するかチェックする $c = new Criteria(); $c->add(UserPeer::LOGIN, $this->getRequestParameter('login')); $user = UserPeer::doSelectOne($c); if ($user) { // パスワードが正しいかチェックする if ($this->getRequestParameter('password') == $user->getPassword()) { // サインイン $this->getContext()->getUser()->signIn(); // ホームページに進む return $this->redirect('main/index'); } else { $this->getRequest()->setError('password', 'wrong password'); } } else { $this->getRequest()->setError('email', 'this user does not exist'); } // エラーが見つかった場合 return $this->forward('security', 'index'); }
note
askeetチュートリアルで説明されているように、よりよいドメインモデルのロジックのためにログインとパスワードのバリデーションをカスタムバリデータで扱うことできます.
では、myUser
クラスのsignIn()
メソッドを見てみましょう:
class myUser extends sfBasicSecurityUser { public function signIn() { $this->setAuthenticated(true); } public function signOut() { $this->setAuthenticated(false); } }
今のところ、これはとても基本的な機能です。セッションごとにユーザーにログインをするか尋ねる限り、適切に動作します。
永続的セッション
永続的セッションを可能にするために、サーバーはユーザーが誰であり以前のログインを成功したことがあるかという情報を記録しているクライアントのコンピュータ(Cookieがやって来た場所)の情報を保存しなければなりません。もちろん、セキュリティ上の理由のために、パスワードをCookieに保存することはできません(ところでこれはaskeetチュートリアルで説明したsha1 ハッシュパスワード保存メソッドと互換性がありません)。ではCookieに何を保存すればよいのでしょうか?2つの要素の比較が認証を実現できるように、Cookieが保存するものは何であれ、データベースに保存しているデータと一致しなければなりません。ですので、リスクを最小限にするために、ランダムな文字列が保存され、15日ごとに再生成されます(寿命の情報はCookieに渡されます)。
新しいUser
テーブルにremember_key
カラムを追加します(そしてモデルをリビルドします)。この新しいフィールドがランダムキーを保存し、キーはユーザーのレコードの一部としてデータベースとクライアントのコンピュータ上の両方のCookieに保存されます。ユーザーが記録をリクエストをしたときにememberキーは設定されます。login
アクションのサインインの行を次のように変更します:
// サインイン $remember = $this->getRequestParameter('remember_me'); $this->getContext()->getUser()->signIn($user, $remember);
これを動作させるために、modules/security/templates/indexSuccess.php
フォームにremember_me
チェックボックスを追加することを忘れないでください。
データベースとCookieの両方でrememberキーを設定するためにmyUser
クラスのsignIn()
メソッドを修正しなければなりません:
public function signIn($user, $remember = false) { $this->setAuthenticated(true); if ($remember) { // ランダムキーを決定する if (!$user->getRememberKey()) { $rememberKey = myTools::generate_random_key(); // キーをUserテーブルに保存する $user->setRememberKey($rememberKey); $user->save(); } // キーをCookieに保存する $value = base64_encode(serialize(array($user->getRememberKey(), $user->getLogin()))); sfContext::getInstance()->getResponse()->setCookie('MyWebSite', $value, time()+60*60*24*15, '/'); } }
generate_random_key()
メソッドはセキュリティの要求の基準を満たすものであれば何でも構いません。では、security/index
アクションを少し変更する必要があります:
public function executeIndex() { if ($this->getRequest()->getCookie('MyWebSite')) { $value = unserialize(base64_decode($this->getRequest()->getCookie('MyWebSite'))); $c = new Criteria(); $c->add(UserPeer::REMEMBER_KEY, $value[0]); $c->add(UserPeer::LOGIN, $value[1]); $user = UserPeer::doSelectOne($c); if ($user) { // サインイン $this->getContext()->getUser()->signIn($user); // ホームページに進む return $this->redirect('main/index'); } } }
この新しいプロセスでCookieが読み込まれます。作業はこれでお終いです。
note
Webサイトのいくつかのページが認証無しでアクセスできる場合、もはやsecurity/index
アクションは毎回実行される最初のアクションではありません。このような場合においてユーザーのログを自動的に記録する場合、単独のアクションでCookieのチェックをするよりもアプリケーションのlib
ディレクトリで新しいrememberFilter
を追加する方が望ましいです:
class rememberFilter extends sfFilter { public function execute ($filterChain) { // 1回だけこのフィルタを実行する if ($this->isFirstCall()) { if ($cookie = $this->getContext()->getRequest()->getCookie('MyWebSite')) { $value = unserialize(base64_decode($cookie)); $c = new Criteria(); $c->add(UserPeer::REMEMBER_KEY, $value[0]); $c->add(UserPeer::LOGIN, $value[1]); $user = UserPeer::doSelectOne($c); if ($user) { // サインイン $this->getContext()->getUser()->signIn($user); } } } // 次のフィルタを実行する $filterChain->execute(); } }
もちろん、filters.yml
設定ファイルでこのフィルタを宣言する必要があります:
rememberFilter: class: rememberFilter
最後の一つです: ユーザーがログアウトした場合、Cookieを削除することをお忘れ無く!
public function signOut() { $this->setAuthenticated(false); sfContext::getInstance()->getResponse()->setCookie('MyWebSite', '', time() - 3600, '/'); }
note
この解決方法は安全ではないページでしか動作しません。カスタムのremember
フィルタの前に安全なページのためのセキュリティチェックが行われるからです。結果的に、適切なCookieを持つユーザーが以前に安全ではないページでログインをすること無しに安全なページにアクセスしようとすると、誰もがログインページにリダイレクトされます。安全なページでもremember me機能が欲しい場合、微妙に異なった実装をする必要があります。sfBasicSecurityFilter
クラスを特化したmyBasicSecurityFilter
クラスを作成し、それにCookieのコントロール機能を設置しなければなりません。それからfactories.yml
において、security_filter
の名前をmyBasicSecurityFilter
に変更しなければなりません。実装の詳細はあなたの判断にお任せします。
永続的セッション: 車輪を再発明しない
上記で説明されたコードを新しいプロジェクトのために書き直すのはとても苦痛です。幸運にも、この目的のためにsfGuardPlugin
を使用することが出来ます。ユーザー管理、データベースのパーミッションとクレデンシャルを自動化するだけでなく、ここで説明したことと似たようなテクニックを使う"remember me"機能も含みます。ですので、永続的セッションを有効にする良い方法はsfGuard プラグインをインストールすることです。
This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.