5

Django アプリケーションでユーザーがアイドル タイムアウトを経験したときに監査したいと考えています。つまり、ユーザーのセッション Cookie の有効期限が settings.py にある SESSION_COOKIE_AGE を超えると、ユーザーはログイン ページにリダイレクトされます。その場合、監査も行う必要があります。「監査」とは、レコードを person.audit テーブルに書き込む必要があることを意味します。

現在、これらのイベントをキャプチャするようにいくつかのミドルウェアを構成しています。残念ながら、ユーザーがログイン ページにリダイレクトされると、Django は新しい Cookie を生成するため、ユーザーがアイドル タイムアウトまたはその他のイベントによってログイン ページに移動したかどうかを判断できません。

私が知る限り、「django_session」テーブルを操作する必要があります。ただし、リダイレクトが発生すると Cookie の sessionid 値がリセットされるため、このテーブルのレコードをそのユーザーに関連付けることはできません。

このジレンマに遭遇したのは私が初めてではないと思います。問題を解決する方法について洞察を持っている人はいますか?

4

3 に答える 3

9

アップデート:

少しテストした後、以下のコードがあなたの質問に答えていないことに気付きました。機能し、シグナル ハンドラーが呼び出されprev_session_dataますが、存在する場合、有用な情報は含まれません。

まず、セッション フレームワークの内部を見てみましょう。

  1. 新しい訪問者がアプリケーション URL を要求すると、新しいセッションが生成されます。この時点では、訪問者はまだ匿名です (request.userは AnonymousUser のインスタンスです)。
  2. 認証が必要なビューを要求すると、ログイン ビューにリダイレクトされます。
  3. ログイン ビューが要求されると、ユーザーのセッションにテスト値が設定されます ( SessionStore._session)。これにより、現在のセッションにフラグaccessedとフラグが自動的に設定されます。modified
  4. 上記の要求の応答フェーズ中に、SessionMiddlewareは現在のセッションを保存Sessionし、テーブルに新しいインスタンスを効果的に作成しdjango_sessionます ( によって提供されるデフォルトのデータベースに基づくセッションを使用している場合django.contrib.sessions.backends.db)。settings.SESSION_COOKIE_NAME新しいセッションの ID はCookieに保存されます。
  5. ユーザーがユーザー名とパスワードを入力してフォームを送信すると、認証されます。認証が成功すると、loginメソッド fromdjango.contrib.authが呼び出されます。login現在のセッションにユーザー ID が含まれているかどうかを確認します。存在する場合、ID はログインしているユーザーの ID と同じでありSessionStore.cycle_key、セッション データを保持しながら、新しいセッション キーを作成するために呼び出されます。それ以外の場合SessionStore.flushは、すべてのデータを削除して新しいセッションを生成するために呼び出されます。これらのメソッドはどちらも (匿名ユーザーの) 前のセッションを削除しSessionStore.create、新しいセッションを作成するために呼び出します。
  6. この時点で、ユーザーは認証され、新しいセッションが確立されます。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

于 2009-03-13T23:31:38.633 に答える
1

SESSION_COOKIE_AGE = 1500 # 25 分

それを設定に入れると、それが処理され、セッションが期限切れになります。

于 2011-06-26T17:55:10.940 に答える
0

I don't know about Django, but can you, simply create a non-persistent cookie, which stores the last access time to a page on your site (you update the cookie on each page load)

Then, on your login page, you can check if your user has your cookie, but no session, then, you know that the user's session has probably timed out. Since you have the time of the last access to a page on your site, you can also calculate, based on the duration of the session, if it has timed out.

于 2009-03-13T22:56:01.950 に答える