アップデート:
少しテストした後、以下のコードがあなたの質問に答えていないことに気付きました。機能し、シグナル ハンドラーが呼び出されprev_session_data
ますが、存在する場合、有用な情報は含まれません。
まず、セッション フレームワークの内部を見てみましょう。
- 新しい訪問者がアプリケーション URL を要求すると、新しいセッションが生成されます。この時点では、訪問者はまだ匿名です (
request.user
は AnonymousUser のインスタンスです)。
- 認証が必要なビューを要求すると、ログイン ビューにリダイレクトされます。
- ログイン ビューが要求されると、ユーザーのセッションにテスト値が設定されます (
SessionStore._session
)。これにより、現在のセッションにフラグaccessed
とフラグが自動的に設定されます。modified
- 上記の要求の応答フェーズ中に、
SessionMiddleware
は現在のセッションを保存Session
し、テーブルに新しいインスタンスを効果的に作成しdjango_session
ます ( によって提供されるデフォルトのデータベースに基づくセッションを使用している場合django.contrib.sessions.backends.db
)。settings.SESSION_COOKIE_NAME
新しいセッションの ID はCookieに保存されます。
- ユーザーがユーザー名とパスワードを入力してフォームを送信すると、認証されます。認証が成功すると、
login
メソッド fromdjango.contrib.auth
が呼び出されます。login
現在のセッションにユーザー ID が含まれているかどうかを確認します。存在する場合、ID はログインしているユーザーの ID と同じでありSessionStore.cycle_key
、セッション データを保持しながら、新しいセッション キーを作成するために呼び出されます。それ以外の場合SessionStore.flush
は、すべてのデータを削除して新しいセッションを生成するために呼び出されます。これらのメソッドはどちらも (匿名ユーザーの) 前のセッションを削除しSessionStore.create
、新しいセッションを作成するために呼び出します。
- この時点で、ユーザーは認証され、新しいセッションが確立されます。ID は、認証に使用されるバックエンドとともにセッションに保存されます。セッション ミドルウェアは、このデータをデータベースに保存し、新しいセッション ID を に保存します
settings.SESSION_COOKIE_NAME
。
ご覧のとおり、以前のソリューションの大きな問題は、create
呼び出されるまでに (ステップ 5.)、以前のセッションの ID がなくなっていることです。他の人が指摘しているように、これは、セッション Cookie の有効期限が切れると、ブラウザーによってサイレントに削除されるために発生します。
アレックス・ゲイナーの提案に基づいて、私は別のアプローチを思いついたと思います.それはあなたが求めていることをしているように見えます. 基本的には、セッション ID をミラーリングするために 2 つ目の有効期間が長い「監査」Cookie と、その Cookie の存在を確認するためのミドルウェアを使用します。リクエストの場合:
- 監査 Cookie もセッション Cookie も存在しない場合、これはおそらく新しいユーザーです。
- 監査 Cookie が存在するが、セッション Cookie が存在しない場合、これはおそらくセッションが期限切れになったばかりのユーザーです
- 両方の Cookie が存在し、同じ値を持っている場合、これはアクティブなセッションです
これまでのコードは次のとおりです。
sessionaudit.middleware.py :
from django.conf import settings
from django.db.models import signals
from django.utils.http import cookie_date
import time
session_expired = signals.Signal(providing_args=['previous_session_key'])
AUDIT_COOKIE_NAME = 'sessionaudit'
class SessionAuditMiddleware(object):
def process_request(self, request):
# The 'print' statements are helpful if you're using the development server
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
audit_cookie = request.COOKIES.get(AUDIT_COOKIE_NAME, None)
if audit_cookie is None and session_key is None:
print "** Got new user **"
elif audit_cookie and session_key is None:
print "** User session expired, Session ID: %s **" % audit_cookie
session_expired.send(self.__class__, previous_session_key=audit_cookie)
elif audit_cookie == session_key:
print "** User session active, Session ID: %s **" % audit_cookie
def process_response(self, request, response):
if request.session.session_key:
audit_cookie = request.COOKIES.get(AUDIT_COOKIE_NAME, None)
if audit_cookie != request.session.session_key:
# New Session ID - update audit cookie:
max_age = 60 * 60 * 24 * 365 # 1 year
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
response.set_cookie(
AUDIT_COOKIE_NAME,
request.session.session_key,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None
)
return response
audit.models.py :
from django.contrib.sessions.models import Session
from sessionaudit.middleware import session_expired
def audit_session_expire(sender, **kwargs):
try:
prev_session = Session.objects.get(session_key=kwargs['previous_session_key'])
prev_session_data = prev_session.get_decoded()
user_id = prev_session_data.get('_auth_user_id')
except Session.DoesNotExist:
pass
session_expired.connect(audit_session_expire)
settings.py :
MIDDLEWARE_CLASSES = (
...
'django.contrib.sessions.middleware.SessionMiddleware',
'sessionaudit.middleware.SessionAuditMiddleware',
...
)
INSTALLED_APPS = (
...
'django.contrib.sessions',
'audit',
...
)
これを使用している場合は、ユーザーがログアウトしたときに監査 Cookie を明示的に削除するカスタム ログアウト ビューを実装する必要があります。また、django signed-cookies ミドルウェアを使用することをお勧めします (しかし、おそらく既にそれを行っているでしょう?)
年:
カスタムセッションバックエンドを使用してこれを行うことができるはずです。(テストされていない) サンプル コードを次に示します。
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.db.models import signals
session_created = signals.Signal(providing_args=['previous_session_key', 'new_session_key'])
class SessionStore(DBStore):
"""
Override the default database session store.
The `create` method is called by the framework to:
* Create a new session, if we have a new user
* Generate a new session, if the current user's session has expired
What we want to do is override this method, so we can send a signal
whenever it is called.
"""
def create(self):
# Save the current session ID:
prev_session_id = self.session_key
# Call the superclass 'create' to create a new session:
super(SessionStore, self).create()
# We should have a new session - raise 'session_created' signal:
session_created.send(self.__class__, previous_session_key=prev_session_id, new_session_key=self.session_key)
上記のコードを「customdb.py」として保存し、それを django プロジェクトに追加します。settings.py で、「SESSION_ENGINE」を上記のファイルへのパスに設定または置き換えます。
SESSION_ENGINE = 'yourproject.customdb'
次に、ミドルウェアまたはmodels.py で、次のように「session_created」シグナルのハンドラーを提供します。
from django.contrib.sessions.models import Session
from yourproject.customdb import session_created
def audit_session_expire(sender, **kwargs):
# remember that 'previous_session_key' can be None if we have a new user
try:
prev_session = Session.objects.get(kwargs['previous_session_key'])
prev_session_data = prev_session.get_decoded()
user_id = prev_session_data['_auth_user_id']
# do something with the user_id
except Session.DoesNotExist:
# new user; do something else...
session_created.connect(audit_session_expire)
を含むアプリを含めることを忘れないでmodels.py
くださいINSTALLED_APPS
。