Как завершить сеанс из-за бездействия в Django?

97

Наше приложение Django имеет следующие требования к управлению сеансом.

  1. Сеансы истекают, когда пользователь закрывает браузер.
  2. Сессии истекают после периода бездействия.
  3. Определите, когда сеанс истекает из-за бездействия, и отобразите соответствующее сообщение для пользователя.
  4. Предупредить пользователей о приближающемся истечении сеанса за несколько минут до окончания периода бездействия. Вместе с предупреждением предоставьте пользователям возможность продлить сеанс.
  5. Если пользователь выполняет длительную бизнес-активность в приложении, которая не связана с отправкой запросов на сервер, сеанс не должен истекать по таймауту.

После прочтения документации, кода Django и некоторых сообщений в блогах, связанных с этим, я пришел к следующему подходу к реализации.

Требование 1
Это требование легко реализовать, установив для SESSION_EXPIRE_AT_BROWSER_CLOSE значение True.

Требование 2
Я видел несколько рекомендаций по использованию SESSION_COOKIE_AGE для установки периода истечения срока действия сеанса. Но у этого метода есть следующие проблемы.

  • Сеанс всегда истекает в конце SESSION_COOKIE_AGE, даже если пользователь активно использует приложение. (Этого можно избежать, задав для истечения срока действия сеанса значение SESSION_COOKIE_AGE для каждого запроса с использованием специального промежуточного программного обеспечения или сохраняя сеанс при каждом запросе, задав для параметра SESSION_SAVE_EVERY_REQUEST значение true. Но следующая проблема неизбежна из-за использования SESSION_COOKIE_AGE.)

  • Из-за того, как работают файлы cookie, SESSION_EXPIRE_AT_BROWSER_CLOSE и SESSION_COOKIE_AGE являются взаимоисключающими, т.е. срок действия cookie истекает при закрытии браузера или по истечении указанного времени. Если используется SESSION_COOKIE_AGE и пользователь закрывает браузер до истечения срока действия cookie, cookie сохраняется, и повторное открытие браузера позволит пользователю (или кому-либо еще) войти в систему без повторной аутентификации.

  • Django полагается только на наличие cookie, чтобы определить, активен ли сеанс. Он не проверяет дату истечения срока действия сеанса, хранящуюся в сеансе.

Для реализации этого требования и решения проблем, упомянутых выше, можно использовать следующий метод.

  • Не устанавливайте SESSION_COOKIE_AGE.
  • Для каждого запроса установите дату окончания сеанса «текущее время + период бездействия».
  • Переопределите process_request в SessionMiddleware и проверьте срок действия сеанса. Отменить сеанс, если он истек.

Требование 3
Когда мы обнаруживаем, что срок действия сеанса истек (в настраиваемом SessionMiddleware выше), установите атрибут запроса, чтобы указать истечение срока действия сеанса. Этот атрибут можно использовать для отображения соответствующего сообщения пользователю.

Требование 4
Используйте JavaScript для обнаружения бездействия пользователя, предоставления предупреждения, а также возможности продления сеанса. Если пользователь желает продлить сеанс, отправьте на сервер импульс поддержки активности, чтобы продлить сеанс.

Требование 5
Используйте JavaScript для обнаружения активности пользователя (во время длительной деловой операции) и отправки импульсов поддержки активности на сервер, чтобы предотвратить истечение срока сеанса.


Вышеупомянутый подход к реализации кажется очень сложным, и мне было интересно, может ли быть более простой метод (особенно для Требования 2).

Любые идеи будут высоко оценены.

Акбар ибрахим
источник
3
+1 за подробное решение
Дон
Есть промежуточное ПО, которое может делать то, что вам нужно. на github и на pypi
gbutler
1
"Из-за того, как работают файлы cookie, SESSION_EXPIRE_AT_BROWSER_CLOSE и SESSION_COOKIE_AGE являются взаимоисключающими, т.е. срок действия файла cookie истекает при закрытии браузера или в указанное время истечения срока. Если используется SESSION_COOKIE_AGE, и пользователь закрывает браузер до истечения срока действия файла cookie, файл cookie сохраняется и повторно открывается браузер разрешит пользователю (или кому-либо еще) войти в систему без повторной аутентификации ". Поправьте меня, если я ошибаюсь, но в новых версиях Django это уже не так? (Минимум 1.5+)
Botond Béres
1
«Django полагается только на наличие cookie, чтобы определить, активен ли сеанс. Он не проверяет дату окончания сеанса, хранящуюся вместе с сеансом». Это уже не так .
knaperek

Ответы:

44

Вот идея ... Завершите сеанс при закрытии браузера с SESSION_EXPIRE_AT_BROWSER_CLOSEнастройкой. Затем установите метку времени в сеансе для каждого такого запроса.

request.session['last_activity'] = datetime.now()

и добавьте промежуточное ПО, чтобы определить, истек ли срок сеанса. что-то вроде этого должно обрабатывать весь процесс ...

from datetime import datetime
from django.http import HttpResponseRedirect

class SessionExpiredMiddleware:
    def process_request(request):
        last_activity = request.session['last_activity']
        now = datetime.now()

        if (now - last_activity).minutes > 10:
            # Do logout / expire session
            # and then...
            return HttpResponseRedirect("LOGIN_PAGE_URL")

        if not request.is_ajax():
            # don't set this for ajax requests or else your
            # expired session checks will keep the session from
            # expiring :)
            request.session['last_activity'] = now

Затем вам просто нужно создать некоторые URL-адреса и представления, чтобы возвращать соответствующие данные для вызовов ajax относительно истечения срока действия сеанса.

когда пользователь решает "обновить" сеанс, так сказать, все, что вам нужно сделать, это requeset.session['last_activity']снова установить текущее время

Очевидно, что этот код - только начало ... но он должен направить вас на правильный путь

Jiaaro
источник
Я просто отношусь к этому скептически, но не думаю, что if not request.is_ajax()это полностью безопасно. Не может ли кто-то, кто удерживает сеанс до истечения срока действия подделки / отправить вызов ajax, и продолжить сеанс?
notbad.jpeg
2
@ notbad.jpeg: в целом "активность" легко подделать. Тот, кто получает сеанс и продолжает отправлять запросы, просто активен.
RemcoGerlich
Это отличный ответ. Промежуточное ПО - это очень малоиспользуемый инструмент при разработке Django.
Джейми Каунселл,
34

Я новичок в использовании Django.

Я хотел, чтобы сеанс истекал, если зарегистрированный пользователь закрывает браузер или находится в режиме ожидания (тайм-аут бездействия) в течение некоторого времени. Когда я погуглил, чтобы понять, этот вопрос о SOF возник первым. Благодаря хорошему ответу я поискал ресурсы, чтобы понять, как работает промежуточное ПО во время цикла запроса / ответа в Django. Это было очень полезно.

Я собирался применить настраиваемое промежуточное ПО в свой код, следуя главному ответу здесь. Но я все еще был немного подозрительным, потому что лучший ответ здесь был отредактирован в 2011 году. Мне потребовалось больше времени, чтобы немного поискать по последним результатам поиска, и я нашел простой способ.

SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True

В других браузерах кроме хрома не проверял. 1. Сессия истекла, когда я закрыл браузер, даже если установлен SESSION_COOKIE_AGE. 2. Только когда я бездействовал более 10 секунд, сеанс истек. Благодаря SESSION_SAVE_EVERY_REQUEST, всякий раз, когда вы выполняете новый запрос, он сохраняет сеанс и обновляет тайм-аут до истечения

Чтобы изменить это поведение по умолчанию, установите для параметра SESSION_SAVE_EVERY_REQUEST значение True. Если установлено значение True, Django будет сохранять сеанс в базе данных при каждом запросе.

Обратите внимание, что файл cookie сеанса отправляется только после создания или изменения сеанса. Если SESSION_SAVE_EVERY_REQUEST имеет значение True, cookie сеанса будет отправляться при каждом запросе.

Аналогичным образом истекающая часть файла cookie сеанса обновляется каждый раз при отправке файла cookie сеанса.

Руководство django 1.10

Я просто оставляю ответ, чтобы некоторые люди, которые новички в Django, как я, не тратили много времени на поиск решения, как это сделал я.

Jayground
источник
26

django-session-security делает именно это ...

... с дополнительным требованием: если сервер не отвечает или злоумышленник отключил интернет-соединение: оно все равно должно истечь.

Предупреждение: я поддерживаю это приложение. Но я очень и очень давно смотрю эту ветку :)

jpic
источник
1
классное приложение - красиво оформленное и хорошо построенное. хороший, чистый код ... спасибо.
Никореллиус 01
Если пользователь закрывает браузер или вкладку (не выходя из системы), когда он / она уходит, заставляет ли это пользователя выйти из системы? Справляется ли он с этим состоянием?
Мехмет Каган Каяалп
Это будет обрабатываться по истечении срока действия cookie сеанса чистого http, не так ли?
jpic 05
12

Один из простых способов удовлетворить ваше второе требование - установить значение SESSION_COOKIE_AGE в settings.py на подходящее количество секунд. Например:

SESSION_COOKIE_AGE = 600      #10 minutes.

Однако при этом сеанс истечет через 10 минут независимо от того, проявляет ли пользователь какую-либо активность. Чтобы решить эту проблему, срок действия может автоматически продлеваться (еще на 10 минут) каждый раз, когда пользователь выполняет любой запрос со следующим предложением:

request.session.set_expiry(request.session.get_expiry_age())
Фернандо Мартин
источник
2
SESSION_COOKIE_AGE = 600. Это продлит срок сеансов с каждым запросом новой страницы или обновлением страницы
Aseem,
1
Я подтверждаю, что просто настройки SESSION_COOKIE_AGEдостаточно и что любой запрос (отправка файла cookie сеанса) автоматически обновит срок действия файла cookie сеанса.
бруно дествильерс
4

также вы можете использовать функции сборки stackoverflow

SESSION_SAVE_EVERY_REQUEST = True
Мексеканез
источник
Совершенно не связано - это для принудительного сохранения сеанса (а не «обновления cookie сеанса») для каждого запроса без проверки того, был ли изменен сеанс.
бруно дествильерс
3

В первом запросе вы можете установить срок действия сеанса как

self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds 

А при использовании access_key и токена

try:
    key = self.request.session['access_key']
except KeyError:
    age = self.request.session.get_expiry_age()
    if age > set_age:
        #redirect to login page
тилапримера
источник