Wie kann ich feststellen, wenn ein Benutzer ein Leerlauf-Timeout in Django hat?
-
22-07-2019 - |
Frage
Ich mag prüfen, wenn ein Benutzer eine Leerlaufzeit in meiner Django-Anwendung erfahren hat. Mit anderen Worten, wenn das Ablaufdatum des Benutzers Session-Cookie der SESSION_COOKIE_AGE in settings.py gefunden überschreitet, wird der Benutzer auf die Login-Seite umgeleitet. Wenn das geschieht, sollte eine Prüfung ebenfalls auftreten. Unter „Prüfung“, dann meine ich eine Aufzeichnung sollte meine person.audit Tabelle geschrieben werden.
Zur Zeit habe ich einige Middleware konfiguriert diese Ereignisse zu erfassen. Leider erzeugt Django ein neues Cookie, wenn der Benutzer auf die Anmeldeseite umgeleitet wird, so kann ich nicht feststellen, ob der Benutzer auf die Login-Seite über eine Leerlaufzeitüberschreitung oder ein anderes Ereignis gemacht.
Von dem, was ich sagen kann, brauchte ich, um mit der „django_session“ Tabelle zu arbeiten. Allerdings können die Datensätze in dieser Tabelle nicht mit diesem Benutzer, weil der Sitzungs-Wert im Cookie zugeordnet werden zurückgesetzt, wenn die Umleitung erfolgt.
Ich vermute, ich bin nicht die erste, dieses Dilemma zu begegnen. Hat jemand Einblick in hat, wie das Problem zu lösen?
Lösung
Update:
Nach einem wenig getestet, merke ich, dass der Code unten Ihre Frage nicht beantworten. Obwohl es funktioniert, und das Signal-Handler aufgerufen wird, prev_session_data
wenn es vorhanden ist, werden alle nützlichen Informationen nicht enthalten.
Als erstes wird ein innen Blick auf die Sitzungen Rahmen:
- Wenn ein neuer Besucher eine Anwendung URL anfordert, eine neue Sitzung für sie erzeugt wird - an diesem Punkt, sind sie immer noch anonym (
request.user
ist eine Instanz AnonymousUser) .
- Wenn sie einen Blick anfordern, die eine Authentifizierung erforderlich ist, sie auf die Login-Ansicht umgeleitet ist.
- Wenn die Anmelde Ansicht angefordert wird, setzt sie einen Testwert in der Sitzung des Benutzers (
SessionStore._session
); dies setzt automatisch dieaccessed
undmodified
Flaggen auf der aktuellen Sitzung. - Während der Antwortphase der obigen Anforderung speichert der
SessionMiddleware
die aktuelle Sitzung, effektiv eine neueSession
Instanz in derdjango_session
Tabelle erstellen (wenn Sie die Standard-Datenbank-backed-Sessions verwenden, vondjango.contrib.sessions.backends.db
zur Verfügung gestellt). Die ID der neuen Sitzung imsettings.SESSION_COOKIE_NAME
Cookie gespeichert. - Wenn der Benutzer in seinem Benutzernamen und Passwort ein und das Formular abschickt, werden sie authentifiziert. Wenn die Authentifizierung erfolgreich ist, wird das Verfahren von
login
django.contrib.auth
genannt.login
prüft, ob die aktuelle Sitzung eine Benutzer-ID enthält; wenn es der Fall ist, und die Identifikation ist die gleiche wie die ID des angemeldeten Benutzers wirdSessionStore.cycle_key
einen neuen Sitzungsschlüssel erstellen genannt, während die Sitzungsdaten beibehalten werden. AndernfallsSessionStore.flush
, genannt alle Daten zu entfernen und eine neue Sitzung zu erzeugen. Beide Methoden sollten die vorherige Sitzung (für den anonymen Benutzer) löschen und rufenSessionStore.create
eine neue Sitzung zu erstellen. - An diesem Punkt wird der Benutzer authentifiziert, und sie haben eine neue Sitzung. Ihre ID wird in der Sitzung gespeichert, zusammen mit dem Backend verwendet, um sie zu authentifizieren. Die Session-Middleware speichert diese Daten in die Datenbank und speichert die neue Session-ID in
settings.SESSION_COOKIE_NAME
.
So können Sie das große Problem mit der bisherigen Lösung zu sehen, ist durch die Zeit create
(Schritt 5) aufgerufen wird, ID die vorherige Sitzung ist längst vorbei. Wie andere haben darauf hingewiesen, geschieht dies, weil, wenn die Session-Cookie abläuft, es lautlos durch den Browser gelöscht.
Aufbauend auf Alex Gaynor Vorschlag , ich glaube, ich habe mit einem anderen Ansatz kommen, das zu tun scheint, was Sie fragen, wenn es um die Kanten noch ein wenig rau ist. Grundsätzlich verwende ich eine zweite langlebiges „audit“ Cookie, die Session-ID zu spiegeln, und einige Middleware für die Anwesenheit dieses Cookie zu überprüfen. Für jede Anfrage:
- , wenn weder die Prüfung Cookie noch der Session-Cookie vorhanden ist, ist dies wahrscheinlich ein neuer Benutzer
- , wenn die Prüfung Cookie vorhanden ist, aber die Session-Cookie nicht der Fall, ist dies wahrscheinlich ein Benutzer, dessen Sitzung abgelaufen nur
- , wenn beide Cookies vorhanden sind, und den gleichen Wert haben, ist dies eine aktive Sitzung
Hier ist der Code so weit:
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',
...
)
Wenn Sie diese verwenden, sollten Sie eine benutzerdefinierte Abmelde Ansicht implementieren, dass explizit löscht das Audit Cookie, wenn sich der Benutzer abmeldet. Auch ich würde vorschlagen, dass die django unterzeichnet-Cookies Middleware (aber Sie sind wahrscheinlich schon tun, dass, nicht wahr?)
ALT:
Ich denke, Sie sollten diese in der Lage sein zu tun, einen benutzerdefinierten Session-Backend verwenden. Hier einige (nicht getestet) Beispielcode:
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)
Speichern Sie den Code über eins ‚customdb.py‘ und fügen Sie das zu Ihrem django Projekt. In Ihrem settings.py setzen oder ‚SESSION_ENGINE‘ mit dem Pfad zu der obigen Datei ersetzen, z.
SESSION_ENGINE = 'yourproject.customdb'
Dann in Ihrer Middleware oder models.py bietet einen Handler für das 'session_created' Signal, etwa so:
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)
Vergessen Sie nicht, die App schließen die models.py
in INSTALLED_APPS
enthält.
Andere Tipps
SESSION_COOKIE_AGE = 1500 # 25 Minuten
Setzen Sie, dass in Ihren Einstellungen und das sollte sich darum kümmern und die Sitzung abläuft.
Ich weiß nicht, über Django, aber können Sie einfach ein nicht-permanentes Cookie erstellen, die die letzte Zugriffszeit auf eine Seite auf Ihrer Website speichert (Sie das Cookie aktualisieren auf jeder Seite zu laden)
Dann auf Ihrer Anmeldeseite können Sie überprüfen, ob Ihre Benutzer Ihre Cookie haben, aber keine Sitzung, dann wissen Sie, dass die Sitzung des Benutzers wahrscheinlich aus abgelaufen ist. Da Sie die Zeit des letzten Zugriffs auf einer Seite auf Ihrer Website haben, können Sie auch berechnen, basierend auf der Dauer der Sitzung, wenn es abgelaufen ist.