Как определить, когда у пользователя в Django время простоя? - PullRequest
5 голосов
/ 13 марта 2009

Я хотел бы провести аудит, когда пользователь испытал время простоя в моем приложении Django. Другими словами, если срок действия файла cookie сеанса пользователя превышает SESSION_COOKIE_AGE, найденный в settings.py, пользователь перенаправляется на страницу входа. Когда это происходит, аудит должен также произойти. Под «аудитом» я подразумеваю, что запись должна быть записана в мою таблицу person.audit.

В настоящее время я настроил некоторое промежуточное ПО для захвата этих событий. К сожалению, Django генерирует новый файл cookie, когда пользователь перенаправляется на страницу входа в систему, поэтому я не могу определить, был ли пользователь перенаправлен на страницу входа через тайм-аут простоя или другое событие.

Из того, что я могу сказать, мне нужно работать с таблицей "django_session". Однако записи в этой таблице не могут быть связаны с этим пользователем, так как значение sessionid в файле cookie сбрасывается при перенаправлении.

Полагаю, я не первый, кто столкнулся с этой дилеммой. Кто-нибудь знает, как решить проблему?

Ответы [ 3 ]

9 голосов
/ 14 марта 2009

Обновление:

После небольшого тестирования я понимаю, что приведенный ниже код не отвечает на ваш вопрос. Хотя он работает и вызывается обработчик сигнала, prev_session_data, если он существует, не будет содержать никакой полезной информации.

Во-первых, внутренний взгляд на структуру сессий:

  1. Когда новый посетитель запрашивает URL-адрес приложения, для него создается новый сеанс - на данный момент они все еще анонимны (request.user является экземпляром AnonymousUser).
  2. Если они запрашивают представление, требующее аутентификации, они перенаправляются в представление входа в систему.
  3. Когда запрашивается представление входа в систему, оно устанавливает тестовое значение в сеансе пользователя (SessionStore._session); это автоматически устанавливает флаги accessed и modified в текущем сеансе.
  4. На этапе ответа на вышеуказанный запрос SessionMiddleware сохраняет текущий сеанс, эффективно создавая новый экземпляр Session в таблице django_session (если вы используете сеансы с поддержкой базы данных по умолчанию, предоставленные django.contrib.sessions.backends.db). Идентификатор нового сеанса сохраняется в файле cookie settings.SESSION_COOKIE_NAME.
  5. Когда пользователь вводит свое имя пользователя и пароль и отправляет форму, они проходят проверку подлинности. Если аутентификация прошла успешно, вызывается метод login из django.contrib.auth. login проверяет, содержит ли текущий сеанс идентификатор пользователя; если это так, и идентификатор совпадает с идентификатором вошедшего в систему пользователя, SessionStore.cycle_key вызывается для создания нового ключа сеанса при сохранении данных сеанса. В противном случае вызывается SessionStore.flush, чтобы удалить все данные и создать новый сеанс. Оба эти метода должны удалить предыдущий сеанс (для анонимного пользователя) и вызвать SessionStore.create, чтобы создать новый сеанс.
  6. На этом этапе пользователь проходит проверку подлинности, и у него есть новый сеанс. Их идентификатор сохраняется в сеансе вместе с серверной частью, используемой для их аутентификации. Промежуточное программное обеспечение сеанса сохраняет эти данные в базу данных и сохраняет свой новый идентификатор сеанса в settings.SESSION_COOKIE_NAME.

Итак, вы видите, что большая проблема с предыдущим решением к тому моменту, когда вызывается create (шаг 5.), идентификатор предыдущего сеанса давно исчез. Как отметили другие , это происходит потому, что по истечении срока действия куки-файла сеанса он автоматически удаляется браузером.

Опираясь на Предложение Алекса Гейнора , я думаю, что нашел другой подход, который, кажется, делает то, что вы просите, хотя он все еще немного грубоват. По сути, я использую второй долговременный файл «аудита» для зеркалирования идентификатора сеанса и некоторое промежуточное программное обеспечение для проверки наличия этого файла 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.

1 голос
/ 26 июня 2011

SESSION_COOKIE_AGE = 1500 # 25 минут

Поместите это в свои настройки, и это должно позаботиться об этом и завершить сеанс.

0 голосов
/ 14 марта 2009

Я не знаю о Django, но вы можете просто создать непостоянный файл cookie, в котором хранится время последнего доступа к странице на вашем сайте (вы обновляете файл cookie при каждой загрузке страницы)

Затем на странице входа в систему вы можете проверить, есть ли у вашего пользователя ваш файл cookie, но нет сеанса, тогда вы знаете, что время сеанса пользователя, вероятно, истекло. Поскольку у вас есть время последнего доступа к странице на вашем сайте, вы также можете рассчитать, исходя из продолжительности сеанса, если он истек.

...