TRAJOIN is an Application to Translate symfony documents Jointly.

home > 1.2/cookbook/en > cookie.txt

Edit this page

Show Markdown text

クッキーで持続的セッションを得る方法は?


概要


symfonyはsfWebRequest and sfWebResponse オブジェクト経由でクッキーへのアクセスを提供します。 そのことによってクッキーの使用をとても簡単にし、持続的セッションを簡単に獲得されます。


クッキーゲッターとセッター


クッキーはクライアントのコンピューターに保存される文字列です。ウェブアプリケーションによって書かれ、同じアプリケーションもしくはドメインによってのみ読み込みが可能です。


symfonyにおいて、クッキーのためのセッターとゲッターは異なるオブジェクトのメソッドですが、筋が通っています。クッキーを得るために、sfWebRequestオブジェクトを使用して、サーバに送られたリクエストを調べます。 他方で、クッキーを設定するために、sfWebResponseオブジェクトを使用して、ユーザに送られるリスポンスを修正します。 アクションの範囲内からクッキーを操作するために、次のショットカットを使用して下さい:



// クッキーのゲッター
$string = $this->getRequest()->getCookie('mycookie');

// クッキーのセッター
$this->getResponse()->setCookie('mycookie', $value);

// オプション付きのクッキーのゲッター
$this->getResponse()->setCookie('mycookie', $value, $expire, $path, $domain, $secure);

setCookie()メソッドの構文は基本的なPHP setcookie()関数のものと同じです (より詳しい情報についてはPHP manualを参照して下さい)。sfWebResponse メソッドを使用することの主なアドバンテージはsymfonyがクッキーのログを記録し、リスポンスが実際に送られるまで読み込みと修正を続けることが出来ることです。


: アクションの外側からクッキーを操作したい場合、ショットカット無しでRequestAnswerオブジェクトにアクセスする必要があります:

$request  = sfContext::getInstance()->getRequest();
$response = sfContext::getInstance()->getResponse();

クッキー使用の例: 持続的セッション


(symfonyにおいて完全な透過的である基本的なセッションハンドリングは別として)クッキーのためのよい用途は持続的セッション機能です。大抵のログインフォームは"remember me"チェックボックスを提供します。それはクリックされる時、ユーザーが将来のセッションのためのログイン処理を回避することを許可します。


基本的なログイン


securityモジュールを除いてすべてのモジュールが安全であるアプリケーションを想像してみましょう。 settings.ymlsecurity/indexアクションへの認証されていないユーザーのリクエストを取り扱うために環境設定されます:


all:
  .settings:
    login_module:           security
    login_action:           index

モデルは採点源でもloginpasswordフィールドを伴う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');
}

: askeet tutorialで説明されているように、ログインとパスワードの確認はよりよいドメインモデルロジックのためにカスタムバリデータで処理することも出来ます。.


では、myUserクラスにあるsignIn()メソッドを見てみましょう:



class myUser extends sfBasicSecurityUser
{
  public function signIn()
  {
    $this->setAuthenticated(true);
  }

  public function signOut()
  {
    $this->setAuthenticated(false);
  }
}

今のところ、これはとても基本的なことです。 すべてのセッションのためにユーザーがログインすることを尋ねる限り、適切に動作します。


持続的セッション


持続的セッションを可能にするために、サーバはユーザーが誰であり以前ログインを成功したことがあるかという情報を記録しているクライアントのコンピューター(クッキーが来るところです)の情報を保存しなければなりません。もちろん、セキュリティの理由のために、パスワードはクッキーに保存することはできません(そして、ところで、これはaskeet tutorialで説明したsha1 hashパスワード保存メソッドと互換性がありません)。ではクッキーに何を保存すればよいのでしょうか?クッキーが保存するものは何であれ、2つの要素の比較が認証を獲得できるようにデータベースに保存しているデータと一致しなければなりません。ですので、リスクを最小限にするために、ランダムな文字列が保存され、15日ごとに再生成されます(寿命はクッキーに与えられます)。


新しいremember_keyカラムをUserテーブルに追加することによります (そしてモデルをリビルドします)。この新しいフィールドがランダムキーを保存し、キーはユーザーのレコードの一部としてデータベースとクライアントのコンピューター上のクッキーの両方に保存されます。rememberキーはユーザーが覚えるようにリクエストをしたときに設定され、次のコードによってloginアクションのサインイン行を変更します:



// サインイン
$remember = $this->getRequestParameter('remember_me');
$this->getContext()->getUser()->signIn($user, $remember);

これを動作させるために、remember_meチェックボックスをmodules/security/templates/indexSuccess.phpフォームに追加することを忘れないで下さい。


myUserクラスのsignIn()メソッドはデータベースとクッキーの両方でrememberキーを設定するために修正されなければなりません:



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();
    }

    // キーをクッキーに保存する
    $value = base64_encode(serialize(array($user->getRememberKey(), $user->getLogin())));
    sfContext::getInstance()->getResponse()->setCookie('MyWebSite', $value, time()+60*60*24*15, '/');
  }
}

The 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');
    }
  }
}

この新しい処理はクッキーがあなたがされたことを読みます。


: ウェブサイトのいくつかのページが認証無しでアクセス出来る場合、security/indexアクションはもはや毎回実行される最初のアクションではありません。このような場合において自動的にユーザーのログを記録する場合、単独のアクションでクッキーチェックをするよりもアプリケーションのlibディレクトリで新しいrememberFilterを追加することの方をおそらく好むでしょう:


class rememberFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    // 一回だけこのフィルタを実行する
    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

最後の一つです: ユーザーがログアウトした場合、クッキーを除去することをお忘れ無く!



public function signOut()
{
  $this->setAuthenticated(false);
  sfContext::getInstance()->getResponse()->setCookie('MyWebSite', '', time() - 3600, '/');
}

: この解決方法は安全ではないページでしか動作しません。カスタムのrememberフィルタの前に安全なページのためのセキュリティチェックが行われるからです。結果として、適切なクッキーを持つユーザーが以前に安全ではないページでログインをすること無しに安全なページにアクセスを使用とすると、誰もがログインページにリダイレクトされます。安全なページでもremember me機能が欲しい場合、微妙に異なった実装をする必要があります。sfBasicSecurityFilterクラスを特化したmyBasicSecurityFilterクラスを作成し、それにクッキーコントロールを設置しなければなりません。それからfactories.ymlにおいて、security_filterの名前をmyBasicSecurityFilterに変更しなければなりません。実装の詳細はあなたの判断にお任せします。


持続的セッション: 車輪を再発明しない


上記で説明されたコードを新しいプロジェクトのために書き直すのはとても苦痛です。幸運にも、この目的のためにsfGuardPluginを使用することが出来ます。ユーザーマネジメント、データベースのパーミッションとクレデンシャルを自動化するだけでなく、ここで説明したことと似たようなテクニックを伴う"remember me"機能も含みます。ですので、持続的セッションを有効にするよい方法はsfGuard pluginをインストールすることです。


Show Markdown text

Menu

Documentation



block contents