Как определить, когда у пользователя в Django время простоя?
Я хотел бы провести аудит, когда пользователь испытал время простоя в моем приложении Django. Другими словами, если срок действия файла cookie сеанса пользователя превышает SESSION_COOKIE_AGE, найденный в settings.py, пользователь перенаправляется на страницу входа. Когда это происходит, аудит должен также произойти. Под "аудитом" я подразумеваю, что запись должна быть записана в мою таблицу person.audit.
В настоящее время я настроил некоторое промежуточное ПО для захвата этих событий. К сожалению, Django генерирует новый файл cookie, когда пользователь перенаправляется на страницу входа в систему, поэтому я не могу определить, был ли пользователь перенаправлен на страницу входа через тайм-аут простоя или какое-либо другое событие.
Из того, что я могу сказать, мне нужно будет работать с таблицей "django_session". Тем не менее, записи в этой таблице не могут быть связаны с этим пользователем, потому что значение sessionid в cookie сбрасывается при перенаправлении.
Я предполагаю, что я не первый, кто столкнулся с этой дилеммой. У кого-нибудь есть понимание, как решить проблему?
3 ответа
Обновить:
После небольшого тестирования я понимаю, что приведенный ниже код не отвечает на ваш вопрос. Хотя это работает, и обработчик сигнала вызывается, prev_session_data
если он существует, не будет содержать никакой полезной информации.
Во-первых, взгляд изнутри на структуру сессий:
- Когда новый посетитель запрашивает URL-адрес приложения, для него создается новый сеанс - на данный момент они все еще анонимны (
request.user
является экземпляром AnonymousUser). - Если они запрашивают представление, требующее проверки подлинности, они перенаправляются в представление входа в систему.
- Когда запрашивается представление входа в систему, оно устанавливает тестовое значение в сеансе пользователя (
SessionStore._session
); это автоматически устанавливаетaccessed
а такжеmodified
флаги текущей сессии. - На этапе ответа на вышеуказанный запрос
SessionMiddleware
сохраняет текущий сеанс, эффективно создавая новыйSession
экземпляр вdjango_session
таблица (если вы используете сеансы с поддержкой базы данных по умолчанию, предоставленныеdjango.contrib.sessions.backends.db
). Идентификатор новой сессии сохраняется вsettings.SESSION_COOKIE_NAME
печенье. - Когда пользователь вводит свое имя пользователя и пароль и отправляет форму, они проходят проверку подлинности. Если аутентификация прошла успешно,
login
метод изdjango.contrib.auth
называется.login
проверяет, содержит ли текущий сеанс идентификатор пользователя; если это так, и идентификатор совпадает с идентификатором вошедшего в систему пользователя,SessionStore.cycle_key
вызывается для создания нового ключа сеанса при сохранении данных сеанса. Иначе,SessionStore.flush
вызывается, чтобы удалить все данные и создать новый сеанс. Оба эти метода должны удалить предыдущий сеанс (для анонимного пользователя) и вызватьSessionStore.create
создать новый сеанс. - На этом этапе пользователь проходит проверку подлинности, и у него есть новый сеанс. Их идентификатор сохраняется в сеансе вместе с серверной частью, используемой для их аутентификации. Промежуточное программное обеспечение сеанса сохраняет эти данные в базу данных и сохраняет их новый идентификатор сеанса в
settings.SESSION_COOKIE_NAME
,
Итак, вы видите, большая проблема с предыдущим решением к тому времени create
вызывается (шаг 5.), идентификатор предыдущего сеанса давно пропал. Как уже отмечали другие, это происходит потому, что по истечении срока действия куки-файла сеанса браузер автоматически удаляет его.
Основываясь на предложении Алекса Гейнора, я думаю, что придумала другой подход, который, кажется, делает то, что вы просите, хотя он по-прежнему немного грубоват. По сути, я использую второй долгоживущий файл 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 для подписанных файлов cookie (но вы, вероятно, уже делаете это, не так ли?)
OLD:
Я думаю, что вы должны быть в состоянии сделать это, используя пользовательский бэкэнд сессии. Вот некоторый (непроверенный) пример кода:
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, но можете ли вы просто создать непостоянный файл cookie, в котором хранится время последнего доступа к странице на вашем сайте (файл cookie обновляется при каждой загрузке страницы)
Затем на странице входа в систему вы можете проверить, есть ли у вашего пользователя ваш файл cookie, но нет сеанса, тогда вы знаете, что время сеанса пользователя, вероятно, истекло. Поскольку у вас есть время последнего доступа к странице на вашем сайте, вы также можете рассчитать, исходя из продолжительности сеанса, если он истек.