Django에서 사용자가 유휴 시간 초과를 겪는 시기를 어떻게 알 수 있나요?

StackOverflow https://stackoverflow.com/questions/644471

  •  22-07-2019
  •  | 
  •  

문제

내 Django 애플리케이션에서 사용자가 유휴 시간 초과를 경험한 경우를 감사하고 싶습니다.즉, 사용자의 세션 쿠키 만료 날짜가 settings.py에 있는 SESSION_COOKIE_AGE를 초과하는 경우 사용자는 로그인 페이지로 리디렉션됩니다.그런 일이 발생하면 감사도 이루어져야 합니다."감사"란 기록이 내 person.audit 테이블에 기록되어야 함을 의미합니다.

현재 이러한 이벤트를 캡처하기 위해 일부 미들웨어를 구성했습니다.안타깝게도 Django는 사용자가 로그인 페이지로 리디렉션될 때 새 쿠키를 생성하므로 유휴 시간 초과 또는 기타 이벤트를 통해 사용자가 로그인 페이지로 이동했는지 확인할 수 없습니다.

내가 알 수 있는 바에 따르면 "django_session" 테이블을 사용하여 작업해야 합니다.그러나 리디렉션이 발생하면 쿠키의 sessionid 값이 재설정되므로 이 테이블의 레코드는 해당 사용자와 연결할 수 없습니다.

이런 딜레마에 직면한 것이 제가 처음은 아닐 것 같습니다.문제 해결 방법에 대한 통찰력을 가진 사람이 있습니까?

도움이 되었습니까?

해결책

업데이트:

약간의 테스트 후에 아래 코드가 귀하의 질문에 대답하지 않는다는 것을 깨달았습니다.작동하고 신호 처리기가 호출되지만 prev_session_data 존재하는 경우 유용한 정보가 포함되지 않습니다.

먼저 세션 프레임워크를 살펴보겠습니다.

  1. 새로운 방문자가 애플리케이션 URL을 요청하면 해당 방문자를 위한 새 세션이 생성됩니다. 이 시점에서는 여전히 익명입니다(request.user AnonymousUser의 인스턴스입니다).
  2. 인증이 필요한 보기를 요청하면 로그인 보기로 리디렉션됩니다.
  3. 로그인 뷰가 요청되면 사용자 세션에 테스트 값을 설정합니다(SessionStore._session);이렇게 하면 자동으로 accessed 그리고 modified 현재 세션의 플래그입니다.
  4. 위 요청의 응답 단계에서 SessionMiddleware 현재 세션을 저장하여 효과적으로 새 세션을 생성합니다. Session 인스턴스의 django_session 테이블(기본 데이터베이스 지원 세션을 사용하는 경우 django.contrib.sessions.backends.db).새 세션의 ID는 settings.SESSION_COOKIE_NAME 쿠키.
  5. 사용자가 사용자 이름과 비밀번호를 입력하고 양식을 제출하면 인증됩니다.인증에 성공하면, login 방법 django.contrib.auth 호출됩니다. login 현재 세션에 사용자 ID가 포함되어 있는지 확인합니다.만약 그렇다면, 해당 ID는 로그인한 사용자의 ID와 동일합니다. SessionStore.cycle_key 세션 데이터를 유지하면서 새 세션 키를 생성하기 위해 호출됩니다.그렇지 않으면, SessionStore.flush 모든 데이터를 제거하고 새 세션을 생성하기 위해 호출됩니다.이 두 가지 방법 모두 이전 세션(익명 사용자의 경우)을 삭제하고 호출해야 합니다. SessionStore.create 새 세션을 생성합니다.
  6. 이 시점에서 사용자는 인증되고 새 세션을 갖게 됩니다.해당 ID는 인증에 사용된 백엔드와 함께 세션에 저장됩니다.세션 미들웨어는 이 데이터를 데이터베이스에 저장하고 새 세션 ID를 settings.SESSION_COOKIE_NAME.

보시다시피 이전 솔루션의 가장 큰 문제는 시간입니다. create 호출되면(5단계) 이전 세션의 ID는 오래 전에 사라졌습니다.처럼 다른 사람들이 지적한, 이는 세션 쿠키가 만료되면 브라우저에서 자동으로 삭제되기 때문에 발생합니다.

구축 알렉스 게이너의 제안, 제 생각에는 또 다른 접근 방식을 생각해낸 것 같습니다. 비록 여전히 가장자리가 약간 거칠기는 하지만 요청하신 대로 작동하는 것 같습니다.기본적으로 저는 두 번째로 오래 지속되는 "감사" 쿠키를 사용하여 세션 ID를 미러링하고 일부 미들웨어를 사용하여 해당 쿠키의 존재를 확인합니다.모든 요청의 경우:

  • 감사 쿠키나 세션 쿠키가 모두 존재하지 않으면 이는 아마도 새로운 사용자일 것입니다.
  • 감사 쿠키는 존재하지만 세션 쿠키는 존재하지 않는 경우 세션이 방금 만료된 사용자일 가능성이 높습니다.
  • 두 쿠키가 모두 존재하고 동일한 값을 갖는 경우 이는 활성 세션입니다.

지금까지의 코드는 다음과 같습니다.

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',
    ...
)

이를 사용하는 경우 사용자가 로그아웃할 때 감사 쿠키를 명시적으로 삭제하는 사용자 정의 로그아웃 보기를 구현해야 합니다.또한 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.

다른 팁

session_cookie_age = 1500 # 25 분

그것을 설정에 넣으면 그것을 처리하고 세션을 만료해야합니다.

Django에 대해 잘 모르겠지만, 귀하의 사이트의 페이지에 마지막 액세스 시간을 저장하는 비 연개 쿠키를 만들 수 있습니다 (각 페이지로드에서 쿠키를 업데이트 함).

그런 다음 로그인 페이지에서 사용자가 쿠키가 있는지 확인할 수 있지만 세션이 없는지 확인할 수 있습니다. 그렇다면 사용자 세션이 시간이 초과되었음을 알 수 있습니다. 사이트의 페이지에 마지막으로 액세스 할 시간이 있으므로 세션 기간에 따라 시간이 초과 된 경우 계산할 수도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top