.. _topics-http-sessions: =================== セッションの使い方 =================== :revision-up-to: 17812 (1.4) .. module:: django.contrib.sessions :synopsis: Provides session management for Django projects. Django は匿名セッション (anonymous session) を完全にサポートしています。 セッションフレームワークを使うと、任意のデータをサイト訪問者単位 (per-site-visitor) で保存したり取得したりできます。セッションフレームワーク はサーバ側にデータを保存し、クッキーの送受信操作を抽象化します。クッキーに はセッション ID だけが保存され、データ自体は送受信されません。 ( :ref:`クッキーベースのバックエンド ` を使うことなしには) セッションを有効にする ====================== セッション機能は :doc:`ミドルウェア ` として実装されていま す。 セッションを有効にするには、以下の作業が必要です: * ``MIDDLEWARE_CLASSES`` を編集して、 ``'django.contrib.sessions.middleware.SessionMiddleware'`` を入れます。 ``django-admiin.py startproject`` の作成するデフォルトの ``settings.py`` では ``SessionMiddleware`` が有効になっています。 セッション機能を必要としないのなら、 :setting:`MIDDLEWARE_CLASSES` から ``SessionMiddleware`` の行を削り、 :setting:`INSTALLED_APPS` からも ``'django.contrib.sessions'`` を削って下さい。セッションを無効にすると、ほんのわずかだけオーバヘッド を軽減できます。 .. _configuring-the-session-engine: セッションエンジンの設定 ========================== デフォルトでは、 Django はセッションをデータベースに (``django.contrib.sessions.models.Session`` モデルを使って) 保存します。こ の仕様は便利ではありますが、場合によっては、データベース以外の場所、ファイ ルシステムやキャッシュ上にセッションデータを保存する方が高速です。 データベースバックエンドセッションを使う ---------------------------------------- もし、データベースバックエンドセッション(database-backend session)を使いたけれ ば、 :setting:`INSTALLED_APPS` セッティングに ``'django.contrib.sessions'`` を 加える必要があります。 一度、 Django の ``manage.py syncdb`` を走らせて、データベースを構成している と、テーブルが一つセッションデータを保存するために使われています。 キャッシュベースのセッション ----------------------------- パフォーマンスを求めるなら、キャッシュベースのセッションバックエンドを使う のがよいでしょう。 Django のキャッシュシステムにセッションデータを保存するには、まずキャッシュ を設定しておく必要があります。詳しくは :ref:`キャッシュのドキュメント ` を参照してください。 .. warning:: Memcached をキャッシュバックエンドとして使っているなら、キャッシュベー スのセッションを使うべきです。ローカルメモリ型のキャッシュバックエンド は十分な時間データを保持できないので、よい選択肢とはいえません。また、 ファイルやデータベースによるキャッシュバックエンドを使っている場合、何 もかもをキャッシュに放り込むより、直接ファイルやデータベースからデータ を出力する方がはるかに高速です。 一度キャッシュが設定されていれば、キャッシュにデータを保存する二つの方法が 選べます。 * :setting:`SESSION_ENGINE` を ``"django.contrib.sessions.backends.cache"`` に、設定すると、シンプルにセッションの保存がキャッシングできます。セッション データは、直接キャッシュの中に保存されます。しかし、セッションデータは 永続的ではありません。キャッシュされたデータはキャッシュが一杯になるか、 キャッシュサーバーが再起動された時に消去されてしまいます。 * キャッシュのデータを永続的にするために、 :setting:`SESSION_ENGINE` を ``"django.contrib.sessions.backends.cached_db"`` に設定してください。 これは、ライトスルーキャッシュを用います(write-through cache)。 全ての書き込みがキャッシュへといき、またデータベースにも書き込まれます。 セッションは、キャッシュの中にデータが準備されていなければ、データベース しか読みません。 両方のセッションの保存はどちらも十分に高速ですが、シンプルキャッシュは 永続性を放棄した結果、より早くなっています。ほとんどのケースで、 ``cached_db`` バックエンドも必要な程度の早さを発揮しますが、少しでも早さを求めるなら、 そしてセッションデータが時とともに消去されていくのをよしとするなら、 ``cache`` バックエンドが適しているケースでしょう。 ファイルベースのセッション --------------------------- ファイルベースのセッションを使うには、 :setting:`SESSION_ENGINE` 設定を ``"django.contrib.sessions.backends.file"`` にします。 また、必要に応じて :setting:`SESSION_FILE_PATH` も設定してください (デフォルト値は ``tempfile.gettempdir()`` の戻り値で、たいていは ``/tmp`` です)。 Web サーバが ``SESSION_FILE_PATH`` の場所にファイルの読書き権限を持っているか 確かめてください。 .. /cookie-session-backend: クッキーベースバックエンドセッションを使う ------------------------------------------ .. versionadded:: 1.4 クッキーベースセッションを使うには、 :setting:`SESSION_ENGINE` を ``"django.contrib.sessions.backends.signed_cookies"`` に設定します。 セッションデータは、 :doc:`cryptographic signing ` と :setting:`SECRET_KEY` 設定へと Django ツールを使って保存されます。 .. note:: :setting:`SESSION_COOKIE_HTTPONLY` 設定を ``True`` にすると、 JavaScript から、保存されたデータを改ざんすることを妨げることが できるのでお勧めしています。 .. warning:: **セッションデータは認証されていますが、暗号化されていません** クッキーバックエンドを使うと、セッションデータはクライアントから 読むことが出来ます。 MAC ( Message Authentication Code )は、クライアントからのデータの変更から データを守るのに使われます、つまりセッションデータは改ざんされる時に無効 化されるということです。同じような無効化は、クライアントが保存している クッキー(すなわち、ユーザのブラウザが保存しているもの)が、全て保存されない 場合と、データが欠落してしまった場合に起こります。 Django はデータを 圧縮していますが、それは `common limit of 4096 bytes`_ を超過しています。 **最新である保証がない** MAC がデータの信頼性と(これはあなたのサイトが生成します) データの一貫性(全てがそこにあり、全てが正しいかどうか)を保証する一方、 MAC はデータの最新性を保証しません。すなわち、クライアントに送信した 最後のものが、返ってきているということです。これは、セッションデータが いくつか用いていることを意味します。クッキーバックエンドは `反射攻撃`_ に耐えて開くだろうということです。 :setting:`SESSION_COOKIE_AGE` よりもクッキーが古い場合、クッキーは 'stale' としてのみ検出されます。 **パフォーマンス** 最後に、大容量のクッキーは、 `サイトのスピード`_ に影響を与えます。 .. _`common limit of 4096 bytes`: http://tools.ietf.org/html/rfc2965#section-5.3 .. _`反射攻撃`: http://en.wikipedia.org/wiki/Replay_attack .. _`サイトのスピード`: http://yuiblog.com/blog/2007/03/01/performance-research-part-3/ ビュー中でセッションを扱う ========================== ``SessionMiddleware`` を有効にすると、各々の :class:`~django.http.HttpRequest` オブジェクト(Django ビュー関数の最初の引数) は辞書ライクオブジェクトの属性 ``session`` を持つようになります。 この属性は読み書き可能です。 ビューのどの部分でも ``request.session`` で読み書き可能です 複数回の編集・変更が可能です。 .. class:: backends.base.SessionBase これは、全てのセッションオブジェクトのベースクラス(基底クラス)です。 通常のディクショナリのメソッドをサポートしています。 .. method:: __getitem__(key) 例: ``fav_color = request.session['fav_color']`` .. method:: __setitem__(key, value) 例: ``request.session['fav_color'] = 'blue'`` .. method:: __delitem__(key) 例: ``del request.session['fav_color']`` セッションデータ中に ``key`` に対応する値がない場合には ``KeyError`` を送出します。 .. method:: __contains__(key) 例: ``'fav_color' in request.session`` .. method:: get(key, default=None) 例: ``fav_color = request.session.get('fav_color', 'red')`` .. method:: pop(key) 例: ``fav_color = request.session.pop('fav_color')`` .. method:: keys .. method:: setdefault .. method:: clear これは、三つのメソッドを持っています: .. method:: flush 現在のセッションデータをデータベースから削除し、後でクッキーに入れて ユーザに送り返すために新たなセッションキーを生成します。ユーザの使っ ているブラウザに、以前のセッションデータにアクセスさせたくない場合に 使います (例えば :func:`django.contrib.auth.logout()` で呼び出されま す)。 .. method:: set_test_cookie テストクッキーを設定して、ユーザのブラウザがクッキーをサポートしてい るかどうかを調べられるようにします。クッキーの動作仕様上、次にブラウ ザがリクエストを送信してくるまでテストは行えません。詳しくは後述の 「 `テストクッキーを設定する`_ 」を参照してください。 .. method:: test_cookie_worked ユーザのブラウザがテストクッキーを受け入れたかどうかに応じて ``True`` または ``False`` を返します。クッキーの動作仕様上、あらかじめ別のペー ジリクエストで ``set_test_cookie()`` を呼び出しておかねばなりません。 後述の「 `テストクッキーを設定する`_ 」を参照してください。 .. method:: delete_test_cookie テストクッキーを削除します。後始末に使って下さい。 .. method:: set_expriry(value) セッションの有効期限をセットします。渡せる値には、以下のバリエーションが あります: * ``value`` に整数を渡すと、セッションがアクティブでないまま ``value`` 秒間経った時点で有効期限が切れます。例えば、 ``request.session.set_expiry(300)`` とすると、セッションの有 効期限は 5 分で切れます。 * ``value`` に ``datetime`` や ``timedelta`` オブジェクトを渡す と、セッションの有効期限は指定日・時刻に切れます。 * ``value`` に ``0`` を渡すと、ユーザセッションクッキーの有効期 限は、ブラウザを閉じた時点で切れます。 * ``value`` に ``None`` を渡すと、セッションの有効期限はグロー バルに設定されているセッションポリシーに戻されます。 セッションの読み込みはアクションの目的が満たされているかは考慮しません。 セッションの満了は、セッションが最後に *変更された* かをもとに計算されて います。 .. method:: get_expiry_age セッションの有効期限が切れるまでの秒数を返します。セッションの有効期 限をカスタマイズしていない場合 (またはブラウザを閉じるまでを有効期限 としている場合) は、 ``settings.SESSION_COOKIE_AGE`` と等しい値です。 .. method:: get_expiry_date セッションの有効期限が切れるまでの日数を返します。セッションの有効期 限をカスタマイズしていない場合 (またはブラウザを閉じるまでを有効期限 としている場合) は、 :setting:`SESSION_COOKIE_AGE` 秒に相当する日数 です。 .. method:: get_expire_at_browser_close ユーザのセッションクッキーの有効期限がブラウザを閉じるまでに設定され ていれば ``True`` を、そうでなければ ``False`` を返します。 セッションオブジェクト使用上のガイドライン ------------------------------------------ * ``request.session`` のキーには通常の Python 文字列を使って下さい。と はいえ、これは厳格な掟 (hard-and-fast rule) ではなく単なる規約です。 * アンダースコアで始まるセッション辞書のキーは Django の内部使用のため に予約されています。 * ``request.session`` を新たなオブジェクトでオーバライドしたり、属性を いじってはなりません。Python 辞書型のように扱って下さい。 使用例 -------- 以下の簡単なビューの例では、ユーザがコメントをポストした後に ``has_commented`` という変数を ``True`` に設定しています。これにより、一人 のユーザに一つのコメントを何度もポストさせないようにします:: def post_comment(request, new_comment): if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=new_comment) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!') 以下のビューでは、「メンバ」をサイトにログインさせます:: def login(request): m = Member.objects.get(username__exact=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponse("You're logged in.") else: return HttpResponse("Your username and password didn't match.") そして下の例では、上の例で ``login()`` したメンバをログアウトさせます:: def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.") 実際には、標準の :meth:`django.contrib.auth.logout()` は、うっかりデータが漏洩 してしまうのを防ぐために、 ``request.session`` の :meth:`~backends.base.SessionBase.flush()` を呼び出しています。 上の例はセッションオブジェクトの仕組みを説明するためのもので、完全な ``logout()`` の実装ではありません。 テストクッキーを設定する ======================== 利便性のために、 Django ではユーザのブラウザがクッキーを受け入れるかどうか を調べるための簡単な方法を提供しています。ビュー内で ``request.session`` の :meth:`~backends.base.SessionBase.session.set_test_cookie()` を呼び出しておき、 それ以後のビュー、すなわち別のビュー呼び出しで :meth:`~backends.base.SessionBase.test_cookie_worked()`` を呼び出すようにしてください。 ``set_test_cookie()`` と ``test_cookie_worked()`` が別々のビュー呼び出しに 分離されるのは不恰好ですが、これはクッキーの動作上仕方のないことです。ある ブラウザに対して一度クッキーを設定しても、そのブラウザが次にリクエストを送 信するまではクッキーを受け入れたかどうかを確かめる術はないのです。 テストが終わったら、 :meth:`~backends.base.SessionBase.delete_test_cookie()` を呼び出して後始末をしておくのがよいでしょう。 クッキーの動作テストが終わった時点で、この関数を呼び出して下さい。 典型的な使用例を以下に示します:: def login(request): if request.method == 'POST': if request.session.test_cookie_worked(): request.session.delete_test_cookie() return HttpResponse("You're logged in.") else: return HttpResponse("Please enable cookies and try again.") request.session.set_test_cookie() return render_to_response('foo/login_form.html') ビューの外でセッションを使う ============================ API を使うと、ビューの外からセッションデータを操作できます:: >>> from django.contrib.sessions.backends.db import SessionStore >>> import datetime >>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead') >>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10) >>> s['last_login'] datetime.datetime(2005, 8, 20, 13, 35, 0) >>> s.save() もし、 ``session_key`` が与えられていなければ、自動で生成されます。:: >>> from django.contrib.sessions.backends.db import SessionStore >>> s = SessionStore() >>> s.save() >>> s.session_key '2b1189a188b44ad18c35e113ac6ceead' ``django.contrib.sessions.backends.db`` バックエンドを使っている場合、各セッ ションは Django のモデルインスタンスで表現されています。 ``Session`` モデル は ``django/contrib/session/models.py`` で定義されています。 ``Session`` は 通常のモデルなので、通常の Django データベース API でアクセスできます:: >>> from django.contrib.sessions.models import Session >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12) セッション情報の辞書を取得するには ``get_decoded()`` を呼び出す必要があるの で注意して下さい。というのも、セッション情報はエンコードされた形式で保存さ れているからです。 >>> s.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' >>> s.get_decoded() {'user_id': 42} セッションはいつ保存されるのか ============================== デフォルトでは、 Django はセッション情報が変更された場合、すなわちセッショ ン情報の入った辞書に値を代入したり、値を削除した場合にのみ、セッションデー タベースを保存します:: # セッションデータは変更されたものとみなされます request.session['foo'] = 'bar' # セッションデータは変更されたものとみなされます del request.session['foo'] # セッションデータは変更されたものとみなされます request.session['foo'] = {} # 落とし穴: request.session ではなく request.session['foo'] の変更なの # で、セッションデータは変更されたものとみなされません。 request.session['foo']['bar'] = 'baz' 上の例の最後のケースでは、セッションオブジェクトに内容が変更されたことを明 示的に教えねばなりません。変更の通知は ``modified`` 属性で行います:: request.session.modified = True この振舞いを変更したければ、 :setting:`SESSION_SAVE_EVERY_REQUEST` 設定を ``True`` に設定してください。 :setting:`SESSION_SAVE_EVERY_REQUEST` を ``True`` にすると、 Django はリクエスト一つ一つに対してセッションを保存します。 セッションクッキーはセッションが作成されたり変更されたりした場合にのみ送信 されることに注意してください。 :setting:`SESSION_SAVE_EVERY_REQUEST` を ``True`` にすると、リクエストごとに必ずセッションクッキーを送信するようになります。 同様に、セッションクッキーの ``expires`` 部分もセッションクッキーの送信ごと に更新されます。 ブラウザアクセス単位のセッションと永続的セッション ================================================== :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` 設定を使うと、セッションフレームワーク に、ブラウザアクセス単位のセッションと永続的セッションのどちらを使わせるか を指定できます。 デフォルトでは、 :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` は ``False`` に設定さ れています。これはセッションクッキーが :setting:`SESSION_COOKIE_AGE` の間だけブラ ウザに保存されることを示します。ユーザがブラウザを起動するたびにログインし なくてもすむようにしたければ、この設定を使ってください。 :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` を ``True`` にすると、 Django はブラウ ザアクセス単位のクッキー、すなわちユーザがブラウザを閉じると有効期限が切れ るようなクッキーを使うようになります。ブラウザを起動するたびにユーザにログ イン操作を行わせたい場合、この設定を使ってください。 この設定は、グローバルに有効なデフォルト値です。 ``request.session`` のメソッドである「 `ビューの外でセッションを使う`_ 」の節で解説した、 :meth:`~backends.base.SessionBase.set_expiry()`` を使えば、セッション単位で有効期限をオーバ ライドできます。 セッションテーブルの消去 ======================== セッションデータは ``django_session`` データベーステーブル上に蓄積されます が、 Django はセッションテーブルを自動的に清掃 *しません。* つまり、期限切 れの (expired) セッションデータを正しい判断基準の下に削除するのは、アプリケー ション開発者であるあなた自身の仕事なのです。 この問題を理解するには、ユーザがセッションを使ったときに何が起きているかを 考える必要があります。ユーザがログインすると、 Django は ``django_session`` データベーステーブルにレコードを 1 行追加します。セッションデータが変更され る度に、このレコード行は更新されてゆきます。ユーザが手動でログアウトすれば、 レコード行は削除されますが、ユーザがログアウト操作を *しなかった場合* には、 レコード行は削除されません。 Django は、クリーンアップ用のアクション、 ``django-admin.py cleanup`` を提 供しています。このスクリプトはセッションテーブルの全てのエントリの中から、 ``expire_date`` の値が過去を指しているものを除去します。もちろん、お使いの アプリケーションが要求する仕様が異なる場合には、別のスクリプトを用意する必 要があるでしょう。 設定 ===== :doc:`Django 設定ファイル ` には、セッションの振舞いを操作す るための設定がいくつかあります: SESSION_ENGINE -------------- デフォルト値: ``django.contrib.sessions.backends.db`` Django がセッションデータを保存する方法を指定します。利用できる値は以下の通 りです: * ``'django.contrib.sessions.backends.db'`` * ``'django.contrib.sessions.backends.file'`` * ``'django.contrib.sessions.backends.cache'`` * ``'django.contrib.sessions.backends.cached_db'`` * ``'django.contrib.sessions.backends.signed_cookies'`` 詳しくは `セッションエンジンの設定`_ を参照してください。 SESSION_FILE_PATH ----------------- デフォルト値: ``/tmp/`` ファイルベースのセッションストレージを使っている場合、この値でセッションデー タの保存場所を指定します。 SESSION_COOKIE_AGE ------------------ デフォルト値: ``1209600`` (秒単位で 2 週間) セッションクッキーの寿命を秒で表したものです。 SESSION_COOKIE_DOMAIN --------------------- デフォルト値: ``None`` セッションクッキーを使うドメインです。クロスドメインのクッキーを使う場合に は ``".lawrence.com"`` といった値に、通常のドメイン内クッキーの場合には ``None`` を指定します。 SESSION_COOKIE_HTTPONLY ----------------------- Default: ``True`` セッションクッキーで HTTPOnly フラグを使うかどうかです。もし、これが ``True`` になっている場合は、クライアントサイドの JavaScript はセッション クッキーにアクセスすることができなくなります。 HTTPOnly_ は、Set-Cookie HTTP レスポンスヘッダーの中に含まれています。 これは :rfc:`2109` のクッキー標準の一部ではありません、これは全ての ブラウザで重んじられているわけではありません。しかし、これが重視されて いれば、クッキーデータをクライアントサイドのスクリプト攻撃からデータを保護 し、リスクを軽減します。 .. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly SESSION_COOKIE_NAME ------------------- Default: ``'sessionid'`` セッションに使うクッキーの名前です。どんな名前にしてもかまいません。 SESSION_COOKIE_PATH ------------------- Default: ``'/'`` このパスはセッションクッキーにセットされます。これは、 Django のインストール されている URL パスか、そのパスの親にマッチします。 これは、同じホストネームで複数の Django インスタンスを走らせる時に便利です。 異なるクッキーパスを用いれるので、インスタンスごとに自身のセッションクッキーを 見ることになります。 SESSION_COOKIE_SECURE --------------------- デフォルト値: ``False`` セッションクッキーにセキュアなクッキーを使うかどうかを決めます。この値を ``True`` に設定すると、クッキーは "セキュア" にマークされます。クッキーがセ キュアにマークされると、ブラウザによっては HTTPS 接続でのみクッキーを転送す るようになります。 SESSION_EXPIRE_AT_BROWSER_CLOSE ------------------------------- Default: ``False`` ブラウザを閉じたときにセッションを期限切れにするかどうかを決めます。 SESSION_SAVE_EVERY_REQUEST -------------------------- デフォルト値: ``False`` リクエストごとにセッションデータを保存するかどうかを決めます。この値が ``False`` (デフォルト) の場合、セッションデータの保存は内容が変更された場合、 すなわちセッションデータ辞書に値を設定したり、値を削除したりした場合だけに なります。 .. _`Django 設定ファイル`: ../settings/ .. _Django settings: ../settings/ 技術的な詳細 ============ * セッション辞書には pickle 化可能な全てのオブジェクトを使えます。詳し くは :mod:`pickle モジュール` を参照してください。 * セッション情報は ``django_session`` という名前のデータベーステーブルに 保存されます。 * Django は必要なときにしかクッキーを送信しません。従って、セッション情 報を設定しない限り、セッションクッキーの送信を行いません。 .. _`pickle モジュールのドキュメント`: http://www.python.jp/doc/nightly/lib/module-pickle.html .. _`the pickle module`: http://www.python.org/doc/current/lib/module-pickle.html URL と Session ID ================= Django のセッションフレームワークは完全なクッキーベースであり、クッキー以外 の情報を扱いません。従って、 PHP のように URL にセッション ID を入れる方法 を最後の手段に残したりはしていません。これは設計上の意図的な決定です。とい うのも、URL にセッション ID を含めると、みっともない URL になるだけでなく、 "Referer" ヘッダを使ってセッション ID を盗まれるという脆弱性を招くからです。