TRAJOIN is an Application to Translate symfony documents Jointly.

home > 1.2/cookbook/en > cookie.txt

[1] Edit ↑TOP

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


[2] Edit ↑TOP

概要


[3] Edit ↑TOP

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


[4] Edit ↑TOP

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


[5] Edit ↑TOP

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


[6] Edit ↑TOP

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


[7] Edit ↑TOP

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

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

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

[8] Edit ↑TOP

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


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

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

[10] Edit ↑TOP

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


[11] Edit ↑TOP

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


[12] Edit ↑TOP

基本的なログイン


[13] Edit ↑TOP

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


[14] Edit ↑TOP
all:
  .settings:
    login_module:           security
    login_action:           index

[15] Edit ↑TOP

モデルは採点源でもloginpasswordフィールドを伴うUserクラスを持ちます。indexSuccess.phpテンプレートはログインフォームを示し(今のところ"remember me"チェックボックスは無しです)、そしてsecurity/loginアクションへの投稿を取り扱います:


[16] Edit ↑TOP

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

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


[18] Edit ↑TOP

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


[19] Edit ↑TOP

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

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

[20] Edit ↑TOP

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


[21] Edit ↑TOP

持続的セッション


[22] Edit ↑TOP

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


[23] Edit ↑TOP

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


[24] Edit ↑TOP

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

[25] Edit ↑TOP

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


[26] Edit ↑TOP

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


[27] Edit ↑TOP

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

[28] Edit ↑TOP

The generate_random_key()メソッドはセキュリティの要求の基準を満たすものであれば何でも構いません。では、security/indexアクションを少し変更する必要があります:


[29] Edit ↑TOP

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

[30] Edit ↑TOP

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


[31] Edit ↑TOP
: ウェブサイトのいくつかのページが認証無しでアクセス出来る場合、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

[32] Edit ↑TOP

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


[33] Edit ↑TOP

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

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


[35] Edit ↑TOP

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


[36] Edit ↑TOP

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


Comments

Menu

Documentation



Latest Histories

The code descibed above c ...
brtRiver(2009-01-05 03:32:19)
Persistent sessions: don& ...
brtRiver(2009-01-05 03:32:07)
>**Note**: This soluti ...
brtRiver(2009-01-05 03:31:56)
[php] public func ...
brtRiver(2009-01-05 03:31:49)
One last thing: If the us ...
brtRiver(2009-01-05 03:31:37)
>**Note**: If some pag ...
brtRiver(2009-01-05 03:31:25)
This new process reads th ...
brtRiver(2009-01-05 03:31:04)
[php] public func ...
brtRiver(2009-01-05 03:30:54)
The `generate_random_key( ...
brtRiver(2009-01-05 03:30:39)
[php] public func ...
brtRiver(2009-01-05 03:30:23)
The `signIn()` method of ...
brtRiver(2009-01-05 03:30:07)
Don't forget to add ...
brtRiver(2009-01-05 03:29:41)
[php] // sign in ...
brtRiver(2009-01-05 03:29:28)
By adding a new `remember ...
brtRiver(2009-01-05 03:29:10)
To allow for persistent s ...
brtRiver(2009-01-05 03:29:01)
### Persistent sessions ...
brtRiver(2009-01-05 03:28:52)
So far this is very basic ...
brtRiver(2009-01-05 03:28:43)
[php] class myUse ...
brtRiver(2009-01-05 03:28:36)
Now, let's have a lo ...
brtRiver(2009-01-05 03:28:24)
>**Note**: The verific ...
brtRiver(2009-01-05 03:28:14)

Untranslated