Aktualisierung:
Nach einigen Tests habe ich festgestellt, dass der unten stehende Code Ihre Frage nicht beantwortet. Obwohl es funktioniert, und das Signal-Handler wird aufgerufen, prev_session_data
wenn sie existiert, keine nützlichen Informationen enthält.
Zunächst ein Blick auf den Sitzungsrahmen:
- Wenn ein neuer Besucher eine Anwendungs-URL anfordert, wird eine neue Sitzung für ihn erstellt - zu diesem Zeitpunkt ist er noch anonym (
request.user
ist eine Instanz von AnonymousUser).
- Wenn sie eine Ansicht anfordern, die eine Authentifizierung erfordert, werden sie zur Login-Ansicht weitergeleitet.
- Wenn die Login-Ansicht angefordert wird, wird ein Testwert in der Sitzung des Benutzers gesetzt (
SessionStore._session
); dies setzt automatisch die accessed
y modified
Flaggen der aktuellen Sitzung.
- Während der Antwortphase der oben genannten Anfrage wird die
SessionMiddleware
speichert die aktuelle Sitzung und erstellt damit eine neue Session
Instanz in der django_session
Tabelle (wenn Sie die standardmäßigen datenbankgestützten Sitzungen verwenden, die von django.contrib.sessions.backends.db
). Die ID der neuen Sitzung wird in der Datei settings.SESSION_COOKIE_NAME
Keks.
- Wenn der Benutzer seinen Benutzernamen und sein Passwort eingibt und das Formular absendet, wird er authentifiziert. Wenn die Authentifizierung erfolgreich ist, wird die
login
Methode von django.contrib.auth
genannt wird. login
prüft, ob die aktuelle Sitzung eine Benutzer-ID enthält; wenn ja, und die ID mit der ID des angemeldeten Benutzers übereinstimmt, SessionStore.cycle_key
wird aufgerufen, um einen neuen Sitzungsschlüssel zu erstellen, wobei die Sitzungsdaten erhalten bleiben. Andernfalls, SessionStore.flush
aufgerufen wird, um alle Daten zu löschen und eine neue Sitzung zu erstellen. Diese beiden Methoden sollten die vorherige Sitzung (für den anonymen Benutzer) löschen und die SessionStore.create
um eine neue Sitzung zu erstellen.
- Zu diesem Zeitpunkt ist der Benutzer authentifiziert und hat eine neue Sitzung. Seine ID wird in der Sitzung gespeichert, zusammen mit dem Backend, das zur Authentifizierung verwendet wurde. Die Sitzungs-Middleware speichert diese Daten in der Datenbank und speichert die neue Sitzungs-ID in
settings.SESSION_COOKIE_NAME
.
Das große Problem bei der bisherigen Lösung ist also, dass zu dem Zeitpunkt, an dem create
aufgerufen wird (Schritt 5.), ist die ID der vorherigen Sitzung längst verschwunden. Da andere haben darauf hingewiesen Dies geschieht, weil der Browser den Sitzungs-Cookie nach dessen Ablauf stillschweigend löscht.
Aufbauend auf Vorschlag von Alex Gaynor Ich glaube, ich habe einen anderen Ansatz gefunden, der genau das zu tun scheint, was Sie wollen, auch wenn er noch ein wenig holprig ist. Grundsätzlich verwende ich einen zweiten langlebigen "Audit"-Cookie, um die Sitzungs-ID zu spiegeln, und einige Middleware, um das Vorhandensein dieses Cookies zu überprüfen. Für jede Anfrage:
- wenn weder das Audit-Cookie noch das Sitzungs-Cookie existieren, handelt es sich wahrscheinlich um einen neuen Benutzer
- Wenn das Audit-Cookie existiert, das Sitzungs-Cookie aber nicht, handelt es sich wahrscheinlich um einen Benutzer, dessen Sitzung gerade abgelaufen ist.
- wenn beide Cookies existieren und den gleichen Wert haben, handelt es sich um eine aktive Sitzung
Hier ist der bisherige Code:
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)
einstellungen.py :
MIDDLEWARE_CLASSES = (
...
'django.contrib.sessions.middleware.SessionMiddleware',
'sessionaudit.middleware.SessionAuditMiddleware',
...
)
INSTALLED_APPS = (
...
'django.contrib.sessions',
'audit',
...
)
Wenn Sie dies verwenden, sollten Sie eine benutzerdefinierte Abmeldeansicht implementieren, die das Audit-Cookie ausdrücklich löscht, wenn sich der Benutzer abmeldet. Ich würde auch vorschlagen, die django signed-cookies Middleware zu verwenden (aber das tun Sie wahrscheinlich schon, oder?)
OLD:
Ich denke, Sie sollten in der Lage sein, dies mit einem benutzerdefinierten Session-Backend zu tun. Hier ist einige (ungetestet) 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 obigen Code als "customdb.py" und fügen Sie ihn zu Ihrem Django-Projekt hinzu. Setzen oder ersetzen Sie in Ihrer settings.py "SESSION_ENGINE" durch den Pfad zur obigen Datei, z. B.:
SESSION_ENGINE = 'yourproject.customdb'
Dann in Ihrem Middleware, oder models.py einen Handler für das Signal "session_created" bereitstellen, 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, die die models.py
sur INSTALLED_APPS
.
0 Stimmen
Was meinen Sie damit, dass Sie es prüfen wollen? Sie meinen, es zu protokollieren? Ehrlich gesagt, verstehe ich nicht ganz, was Sie vorhaben, können Sie das etwas näher erläutern?
0 Stimmen
Die Benutzer können das System auf zwei Arten verlassen: Sie können auf einen Abmeldelink klicken oder ihre Sitzung kann ablaufen (nach 60 Minuten). Ich muss beide Fälle protokollieren (auch bekannt als "Audit").