Django에서 사용자가 유휴 시간 초과를 겪는 시기를 어떻게 알 수 있나요?
-
22-07-2019 - |
문제
내 Django 애플리케이션에서 사용자가 유휴 시간 초과를 경험한 경우를 감사하고 싶습니다.즉, 사용자의 세션 쿠키 만료 날짜가 settings.py에 있는 SESSION_COOKIE_AGE를 초과하는 경우 사용자는 로그인 페이지로 리디렉션됩니다.그런 일이 발생하면 감사도 이루어져야 합니다."감사"란 기록이 내 person.audit 테이블에 기록되어야 함을 의미합니다.
현재 이러한 이벤트를 캡처하기 위해 일부 미들웨어를 구성했습니다.안타깝게도 Django는 사용자가 로그인 페이지로 리디렉션될 때 새 쿠키를 생성하므로 유휴 시간 초과 또는 기타 이벤트를 통해 사용자가 로그인 페이지로 이동했는지 확인할 수 없습니다.
내가 알 수 있는 바에 따르면 "django_session" 테이블을 사용하여 작업해야 합니다.그러나 리디렉션이 발생하면 쿠키의 sessionid 값이 재설정되므로 이 테이블의 레코드는 해당 사용자와 연결할 수 없습니다.
이런 딜레마에 직면한 것이 제가 처음은 아닐 것 같습니다.문제 해결 방법에 대한 통찰력을 가진 사람이 있습니까?
해결책
업데이트:
약간의 테스트 후에 아래 코드가 귀하의 질문에 대답하지 않는다는 것을 깨달았습니다.작동하고 신호 처리기가 호출되지만 prev_session_data
존재하는 경우 유용한 정보가 포함되지 않습니다.
먼저 세션 프레임워크를 살펴보겠습니다.
- 새로운 방문자가 애플리케이션 URL을 요청하면 해당 방문자를 위한 새 세션이 생성됩니다. 이 시점에서는 여전히 익명입니다(
request.user
AnonymousUser의 인스턴스입니다). - 인증이 필요한 보기를 요청하면 로그인 보기로 리디렉션됩니다.
- 로그인 뷰가 요청되면 사용자 세션에 테스트 값을 설정합니다(
SessionStore._session
);이렇게 하면 자동으로accessed
그리고modified
현재 세션의 플래그입니다. - 위 요청의 응답 단계에서
SessionMiddleware
현재 세션을 저장하여 효과적으로 새 세션을 생성합니다.Session
인스턴스의django_session
테이블(기본 데이터베이스 지원 세션을 사용하는 경우django.contrib.sessions.backends.db
).새 세션의 ID는settings.SESSION_COOKIE_NAME
쿠키. - 사용자가 사용자 이름과 비밀번호를 입력하고 양식을 제출하면 인증됩니다.인증에 성공하면,
login
방법django.contrib.auth
호출됩니다.login
현재 세션에 사용자 ID가 포함되어 있는지 확인합니다.만약 그렇다면, 해당 ID는 로그인한 사용자의 ID와 동일합니다.SessionStore.cycle_key
세션 데이터를 유지하면서 새 세션 키를 생성하기 위해 호출됩니다.그렇지 않으면,SessionStore.flush
모든 데이터를 제거하고 새 세션을 생성하기 위해 호출됩니다.이 두 가지 방법 모두 이전 세션(익명 사용자의 경우)을 삭제하고 호출해야 합니다.SessionStore.create
새 세션을 생성합니다. - 이 시점에서 사용자는 인증되고 새 세션을 갖게 됩니다.해당 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에 대해 잘 모르겠지만, 귀하의 사이트의 페이지에 마지막 액세스 시간을 저장하는 비 연개 쿠키를 만들 수 있습니다 (각 페이지로드에서 쿠키를 업데이트 함).
그런 다음 로그인 페이지에서 사용자가 쿠키가 있는지 확인할 수 있지만 세션이 없는지 확인할 수 있습니다. 그렇다면 사용자 세션이 시간이 초과되었음을 알 수 있습니다. 사이트의 페이지에 마지막으로 액세스 할 시간이 있으므로 세션 기간에 따라 시간이 초과 된 경우 계산할 수도 있습니다.