Как использовать сеансы¶
Django обеспечивает полную поддержку анонимных сессий. Фреймворк сессий позволяет хранить и извлекать произвольные данные для каждого посетителя сайта. Он хранит данные на стороне сервера и абстрагирует отправку и получение cookies. Cookies содержат идентификатор сессии, а не сами данные (если только вы не используете cookie based backend).
Разрешение сеансов¶
Сессии реализуются через кусок middleware.
Чтобы включить функцию сеанса, сделайте следующее:
- Отредактируйте параметр
MIDDLEWARE
и убедитесь, что он содержит'django.contrib.sessions.middleware.SessionMiddleware'
. По умолчаниюsettings.py
, созданныйdjango-admin startproject
, активированSessionMiddleware
.
Если вы не хотите использовать сессии, вы также можете удалить строку SessionMiddleware
из MIDDLEWARE
и 'django.contrib.sessions'
из INSTALLED_APPS
. Это сэкономит вам немного накладных расходов.
Настройка механизма сеансов¶
По умолчанию Django хранит сессии в вашей базе данных (используя модель django.contrib.sessions.models.Session
). Хотя это удобно, в некоторых случаях быстрее хранить данные сессии в другом месте, поэтому Django может быть настроен на хранение данных сессии в вашей файловой системе или в вашем кэше.
Использование сеансов с поддержкой базы данных¶
Если вы хотите использовать сессию с опорой на базу данных, вам нужно добавить 'django.contrib.sessions'
к настройке INSTALLED_APPS
.
После настройки установки выполните команду manage.py migrate
для установки единственной таблицы базы данных, в которой хранятся данные сеанса.
Использование кэшированных сессий¶
Для повышения производительности вы можете использовать бэкэнд сессий на основе кэша.
Чтобы хранить данные сессии, используя систему кэширования Django, вам сначала нужно убедиться, что вы настроили свой кэш; подробности см. в cache documentation.
Предупреждение
Сеансы на основе кэша следует использовать только в том случае, если вы используете бэкенд кэша Memcached или Redis. Бэкенд кэша локальной памяти не хранит данные достаточно долго, чтобы быть хорошим выбором, и будет быстрее использовать сессии файлов или баз данных напрямую вместо того, чтобы отправлять все через бэкенды кэша файлов или баз данных. Кроме того, бэкенд кэша локальной памяти НЕ является многопроцессным, поэтому, вероятно, не является хорошим выбором для производственных сред.
Если у вас несколько кэшей, определенных в CACHES
, Django будет использовать кэш по умолчанию. Чтобы использовать другой кэш, задайте в SESSION_CACHE_ALIAS
имя этого кэша.
После того как кэш настроен, необходимо выбрать между кэшем с поддержкой базы данных и непостоянным кэшем.
Бэкэнд базы данных с кэшем (cached_db
) использует сквозной кэш - записи сессий применяются как к кэшу, так и к базе данных. Сессионные чтения используют кэш или базу данных, если данные были удалены из кэша. Чтобы использовать этот бэкэнд, установите SESSION_ENGINE
в "django.contrib.sessions.backends.cached_db"
и следуйте инструкциям по настройке using database-backed sessions.
Бэкэнд кэша (cache
) хранит данные сессии только в вашем кэше. Это быстрее, поскольку позволяет избежать персистентности базы данных, но вам придется подумать о том, что произойдет, когда данные кэша будут вытеснены. Выселение может произойти, если кэш заполнится или сервер кэша будет перезапущен, и это будет означать потерю данных сессии, включая выход пользователей из системы. Чтобы использовать этот бэкенд, установите SESSION_ENGINE
на "django.contrib.sessions.backends.cache"
.
Бэкэнд кэша можно сделать постоянным, используя постоянный кэш, например, Redis с соответствующей конфигурацией. Но если ваш кэш не настроен на достаточную стойкость, выбирайте бэкэнд базы данных с кэшем. Это позволит избежать побочных ситуаций, вызванных ненадежным хранением данных в производстве.
Использование сеансов на основе файлов¶
Чтобы использовать сессии на основе файлов, установите параметр SESSION_ENGINE
в значение "django.contrib.sessions.backends.file"
.
Возможно, вы также захотите установить параметр SESSION_FILE_PATH
(который по умолчанию выводится из tempfile.gettempdir()
, скорее всего, /tmp
), чтобы контролировать, где Django хранит файлы сессий. Обязательно проверьте, что ваш веб-сервер имеет права на чтение и запись в это место.
Использование сеансов на основе cookie¶
Чтобы использовать сессии на основе cookies, установите параметр SESSION_ENGINE
в значение "django.contrib.sessions.backends.signed_cookies"
. Данные сессии будут храниться с помощью инструментов Django для cryptographic signing и настройки SECRET_KEY
.
Примечание
Рекомендуется оставить настройку SESSION_COOKIE_HTTPONLY
на True
, чтобы предотвратить доступ к хранимым данным из JavaScript.
Предупреждение
Если в файлах SECRET_KEY
или SECRET_KEY_FALLBACKS
не сохраняется секретность и используется django.contrib.sessions.serializers.PickleSerializer
, то это может привести к произвольному удаленному выполнению кода..
Злоумышленник, владеющий SECRET_KEY
или SECRET_KEY_FALLBACKS
, может не только генерировать фальсифицированные данные сессии, которым будет доверять ваш сайт, но и удаленно выполнить произвольный код, поскольку данные сериализуются с помощью pickle.
Если вы используете сеансы на основе cookie, обратите особое внимание на то, чтобы ваш секретный ключ всегда хранился в полном секрете для любой системы, которая может быть удаленно доступна.
Данные сессии подписаны, но не зашифрованы
При использовании бэкенда cookies данные сессии могут быть прочитаны клиентом.
MAC (Message Authentication Code) используется для защиты данных от изменений со стороны клиента, так что данные сессии будут признаны недействительными при подделке. Такая же недействительность произойдет, если клиент, хранящий cookie (например, браузер вашего пользователя), не сможет сохранить все данные сессионного cookie и сбросит их. Несмотря на то, что Django сжимает данные, все еще вполне возможно превысить common limit of 4096 bytes на одну куку.
Нет гарантии свежести
Обратите также внимание, что хотя MAC может гарантировать подлинность данных (что они были сгенерированы вашим сайтом, а не кем-то другим) и целостность данных (что они все на месте и корректны), он не может гарантировать свежесть, т.е. то, что вам возвращается последнее, что вы отправили клиенту. Это означает, что для некоторых случаев использования данных сеанса бэкенд cookie может открыть вам путь к replay attacks. В отличие от других бэкендов сессий, которые хранят запись каждой сессии на стороне сервера и аннулируют ее, когда пользователь выходит из системы, сессии на основе cookie не аннулируются, когда пользователь выходит из системы. Таким образом, если злоумышленник украдет cookie пользователя, он сможет использовать его для входа в систему под именем этого пользователя, даже если тот выйдет из системы. Cookies будут определяться как «несвежие» только в том случае, если они старше вашего SESSION_COOKIE_AGE
.
Производительность
Наконец, размер cookie может влиять на скорость работы вашего сайта.
Использование сессий в представлениях¶
Когда SessionMiddleware
активирован, каждый объект HttpRequest
- первый аргумент любой функции представления Django - будет иметь атрибут session
, который является словареподобным объектом.
Вы можете читать его и записывать в request.session
в любой точке вашего представления. Вы можете редактировать его несколько раз.
-
class
backends.base.
SessionBase
¶ Это базовый класс для всех объектов сессии. Он имеет следующие стандартные методы словаря:
-
__getitem__
(key)¶ Пример:
fav_color = request.session['fav_color']
-
__setitem__
(key, value)¶ Пример:
request.session['fav_color'] = 'blue'
-
__delitem__
(key)¶ Пример:
del request.session['fav_color']
. Это поднимаетKeyError
, если данныйkey
еще не находится в сессии.
-
__contains__
(key)¶ Пример:
'fav_color' in request.session
-
get
(key, default=None)¶ Пример:
fav_color = request.session.get('fav_color', 'red')
-
pop
(key, default=__not_given)¶ Пример:
fav_color = request.session.pop('fav_color', 'blue')
-
keys
()¶
-
items
()¶
-
setdefault
()¶
-
clear
()¶
Он также имеет такие методы:
-
flush
()¶ Удаляет данные текущей сессии и удаляет куки сессии. Это используется, если вы хотите, чтобы данные предыдущей сессии не могли быть снова доступны из браузера пользователя (например, функция
django.contrib.auth.logout()
вызывает ее).
-
set_test_cookie
()¶ Устанавливает тестовый файл cookie, чтобы определить, поддерживает ли браузер пользователя файлы cookie. В связи с тем, как работают куки, вы не сможете проверить это до следующего запроса страницы пользователем. Дополнительную информацию см. в разделе Setting test cookies ниже.
-
test_cookie_worked
()¶ Возвращает либо
True
, либоFalse
, в зависимости от того, принял ли браузер пользователя тестовый файл cookie. В связи с тем, как работают куки, вам придется вызватьset_test_cookie()
на предыдущем, отдельном запросе страницы. См. раздел Setting test cookies ниже для получения дополнительной информации.
-
delete_test_cookie
()¶ Удаляет тестовый файл cookie. Используйте это, чтобы убрать за собой.
-
get_session_cookie_age
()¶ Возвращает значение параметра
SESSION_COOKIE_AGE
. Это значение может быть переопределено в пользовательском бэкенде сессии.
-
set_expiry
(value)¶ Устанавливает время истечения срока действия сессии. Вы можете передать несколько различных значений:
- Если
value
- целое число, то сессия завершится через столько-то секунд бездействия. Например, вызовrequest.session.set_expiry(300)
приведет к тому, что сессия завершится через 5 минут. - Если
value
является объектомdatetime
илиtimedelta
, то сессия истечет в определенную дату/время. - Если
value
равно0
, срок действия cookie сессии пользователя истекает после закрытия веб-браузера пользователя. - Если
value
равноNone
, сессия возвращается к использованию глобальной политики истечения срока действия сессии.
Чтение сессии не считается активностью для целей истечения срока действия. Срок действия сессии отсчитывается от последнего времени, когда сессия была изменена.
- Если
-
get_expiry_age
()¶ Возвращает количество секунд до истечения срока действия данной сессии. Для сессий без пользовательского срока действия (или настроенных на истечение при закрытии браузера) это значение будет равно
SESSION_COOKIE_AGE
.Эта функция принимает два необязательных аргумента в виде ключевых слов:
modification
: последняя модификация сессии, как объектdatetime
. По умолчанию - текущее время.expiry
: информация об истечении срока действия сессии, в виде объектаdatetime
,int
(в секундах) илиNone
. По умолчанию используется значение, сохраненное в сессии поset_expiry()
, если оно есть, илиNone
.
Примечание
Этот метод используется бэкендами сессий для определения срока действия сессии в секундах при сохранении сессии. Он не предназначен для использования вне этого контекста.
В частности, хотя возможно определить оставшееся время жизни сессии только тогда, когда у вас есть правильное значение
modification
**иexpiry
установлен как объектdatetime
, когда у вас есть значениеmodification
, более просто рассчитать срок действия вручную:expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)
-
get_expiry_date
()¶ Возвращает дату истечения срока действия этой сессии. Для сессий без пользовательского срока действия (или настроенных на истечение срока действия при закрытии браузера) эта дата будет равна
SESSION_COOKIE_AGE
секунд от настоящего момента.Эта функция принимает те же аргументы ключевых слов, что и
get_expiry_age()
, и к ней применимы аналогичные указания по использованию.
-
get_expire_at_browser_close
()¶ Возвращает либо
True
, либоFalse
, в зависимости от того, истечет ли срок действия cookie сессии пользователя при закрытии веб-браузера пользователя.
-
clear_expired
()¶ Удаляет истекшие сессии из хранилища сессий. Этот метод класса вызывается командой
clearsessions
.
-
cycle_key
()¶ Создает новый ключ сессии, сохраняя текущие данные сессии.
django.contrib.auth.login()
вызывает этот метод для защиты от фиксации сеанса.
-
Сериализация сеанса¶
По умолчанию Django сериализует данные сессии, используя JSON. Вы можете использовать параметр SESSION_SERIALIZER
для настройки формата сериализации сессии. Даже с учетом предостережений, описанных в Напишите свой собственный сериализатор, мы настоятельно рекомендуем придерживаться сериализации JSON особенно если вы используете бэкенд cookie.
Например, вот сценарий атаки, если вы используете pickle
для сериализации данных сессии. Если вы используете signed cookie session backend и SECRET_KEY
(или любой ключ из SECRET_KEY_FALLBACKS
) известен злоумышленнику (в Django нет врожденной уязвимости, которая привела бы к его утечке), то злоумышленник может вставить в сессию строку, которая, будучи распакованной, выполнит произвольный код на сервере. Техника для этого проста и легко доступна в интернете. Хотя хранилище сессий cookie подписывает данные, хранящиеся в cookie, чтобы предотвратить подделку, утечка SECRET_KEY
немедленно перерастает в уязвимость удаленного выполнения кода.
Сериализаторы в комплекте¶
-
class
serializers.
JSONSerializer
¶ Обертка вокруг сериализатора JSON из
django.core.signing
. Может сериализовать только основные типы данных.Кроме того, поскольку JSON поддерживает только строковые ключи, следует иметь в виду, что использование нестроковых ключей в
request.session
не будет работать так, как ожидается:>>> # initial assignment >>> request.session[0] = "bar" >>> # subsequent requests following serialization & deserialization >>> # of session data >>> request.session[0] # KeyError >>> request.session["0"] 'bar'
Аналогично, данные, которые не могут быть закодированы в JSON, такие как байты, не относящиеся к стандарту UTF8, например
'\xd9'
(который поднимаетUnicodeDecodeError
), не могут быть сохранены.Более подробно об ограничениях сериализации JSON смотрите в разделе Напишите свой собственный сериализатор.
-
class
serializers.
PickleSerializer
¶ Поддерживает произвольные объекты Python, но, как описано выше, может привести к уязвимости удаленного выполнения кода, если
SECRET_KEY
или любой ключ изSECRET_KEY_FALLBACKS
станет известен злоумышленнику.Не рекомендуется, начиная с версии 4.1: Из-за риска удаленного выполнения кода этот сериализатор устарел и будет удален в Django 5.0.
Напишите свой собственный сериализатор¶
Обратите внимание, что JSONSerializer
не может работать с произвольными типами данных Python. Как это часто бывает, существует компромисс между удобством и безопасностью. Если вы хотите хранить более сложные типы данных, включая datetime
и Decimal
, в сессиях с поддержкой JSON, вам придется написать собственный сериализатор (или преобразовать такие значения в сериализуемый объект JSON перед хранением в request.session
). В то время как сериализация этих значений часто проста (может помочь DjangoJSONEncoder
), написание декодера, который может надежно получить обратно то же самое, что вы ввели, более хрупко. Например, вы рискуете вернуть datetime
, который на самом деле был строкой, которая просто оказалась в том же формате, который был выбран для datetime
s).
Ваш класс сериализатора должен реализовать два метода, dumps(self, obj)
и loads(self, data)
, для сериализации и десериализации словаря данных сессии, соответственно.
Рекомендации по объектам сеанса¶
- Используйте обычные строки Python в качестве ключей словаря на
request.session
. Это скорее условность, чем жесткое правило. - Ключи словаря сессий, начинающиеся с символа подчеркивания, зарезервированы для внутреннего использования Django.
- Не переопределяйте
request.session
с новым объектом, не обращайтесь к его атрибутам и не устанавливайте их. Используйте его как словарь Python.
Примеры¶
Это упрощенное представление устанавливает переменную has_commented
в True
после того, как пользователь публикует комментарий. Оно не позволяет пользователю публиковать комментарий более одного раза:
def post_comment(request, new_comment):
if request.session.get("has_commented", False):
return HttpResponse("You've already commented.")
c = comments.Comment(comment=new_comment)
c.save()
request.session["has_commented"] = True
return HttpResponse("Thanks for your comment!")
Это упрощенное представление регистрирует «члена» сайта:
def login(request):
m = Member.objects.get(username=request.POST["username"])
if m.check_password(request.POST["password"]):
request.session["member_id"] = m.id
return HttpResponse("You're logged in.")
else:
return HttpResponse("Your username and password didn't match.")
…А этот выводит пользователя из системы, согласно login()
выше:
def logout(request):
try:
del request.session["member_id"]
except KeyError:
pass
return HttpResponse("You're logged out.")
Стандартная функция django.contrib.auth.logout()
на самом деле делает немного больше, чем это, чтобы предотвратить непреднамеренную утечку данных. Она вызывает метод flush()
из request.session
. Мы используем этот пример как демонстрацию работы с объектами сессии, а не как полную реализацию logout()
.
Установка тестовых файлов cookie¶
В качестве удобства Django предоставляет способ проверить, принимает ли браузер пользователя cookies. Вызовите метод set_test_cookie()
из request.session
в представлении и вызовите test_cookie_worked()
в последующем представлении - не в том же вызове представления.
Это неудобное разделение между set_test_cookie()
и test_cookie_worked()
необходимо из-за того, как работают куки. Когда вы устанавливаете cookie, вы не можете сказать, принял ли его браузер, до следующего запроса браузера.
Хорошей практикой является использование delete_test_cookie()
, чтобы убрать за собой. Сделайте это после того, как убедитесь, что тестовая печенька сработала.
Вот типичный пример использования:
from django.http import HttpResponse
from django.shortcuts import render
def login(request):
if request.method == "POST":
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponse("You're logged in.")
else:
return HttpResponse("Please enable cookies and try again.")
request.session.set_test_cookie()
return render(request, "foo/login_form.html")
Использование сеансов из представлений¶
Примечание
Примеры в этом разделе импортируют объект SessionStore
непосредственно из бэкенда django.contrib.sessions.backends.db
. В своем собственном коде вы должны рассмотреть возможность импорта SessionStore
из механизма сессии, обозначенного SESSION_ENGINE
, как показано ниже:
>>> from importlib import import_module
>>> from django.conf import settings
>>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
Для работы с данными сессии вне представления имеется API:
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s["last_login"] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key="2b1189a188b44ad18c35e113ac6ceead")
>>> s["last_login"]
1376587691
SessionStore.create()
предназначен для создания новой сессии (т.е. сессии, не загруженной из хранилища сессий и с session_key=None
). save()
предназначен для сохранения существующей сессии (т.е. сессии, загруженной из хранилища сессий). Вызов save()
на новой сессии также может сработать, но имеет небольшую вероятность генерации session_key
, которая столкнется с существующей сессией. create()
вызывает save()
и зацикливается, пока не будет сгенерирован неиспользуемый session_key
.
Если вы используете бэкенд django.contrib.sessions.backends.db
, то каждая сессия представляет собой обычную модель Django. Модель Session
определяется в django/contrib/sessions/models.py. Поскольку это обычная модель, вы можете обращаться к сессиям, используя обычный API базы данных Django:
>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk="2b1189a188b44ad18c35e113ac6ceead")
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)
Обратите внимание, что для получения словаря сессии необходимо вызвать get_decoded()
. Это необходимо, поскольку словарь хранится в закодированном виде:
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}
Когда сохраняются сеансы¶
По умолчанию Django сохраняет данные в базе данных сессии только в том случае, если сессия была изменена - то есть, если любое из значений ее словаря было присвоено или удалено:
# Session is modified.
request.session["foo"] = "bar"
# Session is modified.
del request.session["foo"]
# Session is modified.
request.session["foo"] = {}
# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session["foo"]["bar"] = "baz"
В последнем случае приведенного выше примера мы можем явно сообщить объекту сессии, что он был изменен, установив атрибут modified
на объекте сессии:
request.session.modified = True
Чтобы изменить это поведение по умолчанию, установите параметр SESSION_SAVE_EVERY_REQUEST
в значение True
. Если установить значение True
, Django будет сохранять сессию в базе данных при каждом запросе.
Обратите внимание, что сессионный cookie отправляется только тогда, когда сессия была создана или изменена. Если SESSION_SAVE_EVERY_REQUEST
равно True
, сессионный cookie будет отправляться при каждом запросе.
Аналогично, часть expires
сеансового cookie обновляется при каждой отправке сеансового cookie.
Сессия не сохраняется, если код состояния ответа равен 500.
Сеансы длиной в браузер и постоянные сеансы¶
С помощью параметра SESSION_EXPIRE_AT_BROWSER_CLOSE
вы можете контролировать, использует ли фреймворк сессий сеансы длиной в браузер или постоянные сеансы.
По умолчанию значение SESSION_EXPIRE_AT_BROWSER_CLOSE
установлено на False
, что означает, что сеансовые куки будут храниться в браузерах пользователей до тех пор, пока SESSION_COOKIE_AGE
. Используйте это, если вы не хотите, чтобы людям приходилось входить в систему каждый раз, когда они открывают браузер.
Если SESSION_EXPIRE_AT_BROWSER_CLOSE
установлено в значение True
, Django будет использовать cookies длиной в браузер - cookies, срок действия которых истекает, как только пользователь закрывает браузер. Используйте это, если вы хотите, чтобы людям приходилось входить в систему каждый раз, когда они открывают браузер.
Эта настройка является глобальной по умолчанию и может быть перезаписана на уровне каждой сессии путем явного вызова метода set_expiry()
из request.session
, как описано выше в using sessions in views.
Примечание
Некоторые браузеры (например, Chrome) предоставляют настройки, позволяющие пользователям продолжать сеансы просмотра после закрытия и повторного открытия браузера. В некоторых случаях это может мешать настройке SESSION_EXPIRE_AT_BROWSER_CLOSE
и предотвращать завершение сеансов при закрытии браузера. Пожалуйста, помните об этом при тестировании приложений Django, в которых включена настройка SESSION_EXPIRE_AT_BROWSER_CLOSE
.
Очистка хранилища сеансов¶
По мере того как пользователи создают новые сессии на вашем сайте, данные сессии могут накапливаться в хранилище сессий. Если вы используете бэкенд базы данных, таблица базы данных django_session
будет расти. Если вы используете файловый бэкенд, ваш временный каталог будет содержать все большее количество файлов.
Чтобы понять эту проблему, рассмотрим, что происходит с бэкендом базы данных. Когда пользователь входит в систему, Django добавляет строку в таблицу базы данных django_session
. Django обновляет эту строку каждый раз, когда данные сессии изменяются. Если пользователь выходит из системы вручную, Django удаляет эту строку. Но если пользователь не вышел из системы, строка никогда не будет удалена. Аналогичный процесс происходит и с файловым бэкендом.
Django не обеспечивает автоматическую очистку истекших сессий. Поэтому ваша работа заключается в регулярной очистке истекших сессий. Django предоставляет для этого команду управления очисткой: clearsessions
. Рекомендуется вызывать эту команду на регулярной основе, например, в качестве ежедневного задания cron.
Обратите внимание, что бэкэнд кэша не подвержен этой проблеме, поскольку кэш автоматически удаляет устаревшие данные. Бэкэнд cookie также не подвержен этой проблеме, поскольку данные сессии хранятся в браузерах пользователей.
Настройки¶
Несколько Django settings дают вам контроль над поведением сессии:
Безопасность сеанса¶
Поддомены внутри сайта могут устанавливать cookies на клиенте для всего домена. Это делает фиксацию сеанса возможной, если cookies разрешены с поддоменов, не контролируемых доверенными пользователями.
Например, злоумышленник может войти в good.example.com
и получить действительную сессию для своей учетной записи. Если злоумышленник имеет контроль над bad.example.com
, он может использовать его для отправки вам ключа сессии, поскольку поддомену разрешено устанавливать cookies на *.example.com
. Когда вы посетите good.example.com
, вы войдете в систему как злоумышленник и можете случайно ввести свои конфиденциальные личные данные (например, информацию о кредитной карте) в аккаунт злоумышленника.
Другой возможной атакой будет, если good.example.com
установит свой SESSION_COOKIE_DOMAIN
на "example.com"
, что приведет к отправке сессионных cookie с этого сайта на bad.example.com
.
Технические детали¶
- Словарь сессии принимает любое сериализуемое значение
json
при использованииJSONSerializer
. - Данные сессии хранятся в таблице базы данных с именем
django_session
. - Django отправляет cookie только в том случае, если это необходимо. Если вы не задаете никаких данных сессии, он не будет отправлять сессионный cookie.
Объект SessionStore
¶
При внутренней работе с сессиями Django использует объект session store из соответствующего движка сессий. По соглашению, класс объекта хранилища сессий называется SessionStore
и находится в модуле, обозначенном SESSION_ENGINE
.
Все классы SessionStore
, доступные в Django, наследуются от SessionBase
и реализуют методы манипулирования данными, а именно:
exists()
create()
save()
delete()
load()
clear_expired()
Для того чтобы создать пользовательский механизм сессий или адаптировать существующий, вы можете создать новый класс, наследующий от SessionBase
или любого другого существующего класса SessionStore
.
Вы можете расширить сеансовые движки, но для этого при использовании сеансовых движков с поддержкой базы данных обычно требуются дополнительные усилия (подробности см. в следующем разделе).
Расширение сеансовых движков с опорой на базу данных¶
Создание собственного механизма сессий с поддержкой базы данных на основе тех, которые включены в Django (а именно db
и cached_db
) может быть сделано путем наследования AbstractBaseSession
и одного из классов SessionStore
.
AbstractBaseSession
и BaseSessionManager
импортируются из django.contrib.sessions.base_session
так, что их можно импортировать, не включая django.contrib.sessions
в INSTALLED_APPS
.
-
class
base_session.
AbstractBaseSession
¶ Абстрактная базовая модель сеанса.
-
session_key
¶ Первичный ключ. Само поле может содержать до 40 символов. Текущая реализация генерирует 32-символьную строку (случайная последовательность цифр и строчных букв ASCII).
-
session_data
¶ Строка, содержащая закодированный и сериализованный словарь сессии.
-
expire_date
¶ Время, обозначающее дату окончания сеанса.
Просроченные сессии недоступны пользователю, однако они могут сохраняться в базе данных до тех пор, пока не будет выполнена команда управления
clearsessions
.
-
classmethod
get_session_store_class
()¶ Возвращает класс хранилища сессии, который будет использоваться с данной моделью сессии.
-
get_decoded
()¶ Возвращает декодированные данные сессии.
Декодирование выполняется классом хранилища сессий.
-
Вы также можете настроить менеджер моделей, создав подкласс BaseSessionManager
:
-
class
base_session.
BaseSessionManager
¶ -
encode
(session_dict)¶ Возвращает заданный словарь сессии, сериализованный и закодированный в виде строки.
Кодирование выполняется классом хранилища сессий, привязанным к классу модели.
-
save
(session_key, session_dict, expire_date)¶ Сохраняет данные сессии для предоставленного ключа сессии или удаляет сессию, если данные пусты.
-
Настройка классов SessionStore
достигается путем переопределения методов и свойств, описанных ниже:
-
class
backends.db.
SessionStore
¶ Реализует хранение сессий с опорой на базу данных.
-
classmethod
get_model_class
()¶ Переопределите этот метод, чтобы вернуть пользовательскую модель сессии, если она вам нужна.
-
create_model_instance
(data)¶ Возвращает новый экземпляр объекта модели сессии, который представляет текущее состояние сессии.
Переопределение этого метода дает возможность изменять данные модели сессии до их сохранения в базе данных.
-
classmethod
-
class
backends.cached_db.
SessionStore
¶ Реализует кэшированное хранилище сессий на основе базы данных.
-
cache_key_prefix
¶ Префикс, добавляемый к ключу сессии для построения строки ключа кэша.
-
Пример¶
В приведенном ниже примере показан пользовательский механизм сессий на основе базы данных, который включает дополнительный столбец базы данных для хранения идентификатора учетной записи (таким образом обеспечивая возможность запроса базы данных для всех активных сессий для учетной записи):
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models
class CustomSession(AbstractBaseSession):
account_id = models.IntegerField(null=True, db_index=True)
@classmethod
def get_session_store_class(cls):
return SessionStore
class SessionStore(DBStore):
@classmethod
def get_model_class(cls):
return CustomSession
def create_model_instance(self, data):
obj = super().create_model_instance(data)
try:
account_id = int(data.get("_auth_user_id"))
except (ValueError, TypeError):
account_id = None
obj.account_id = account_id
return obj
Если вы переходите со встроенного в Django хранилища сессий cached_db
на пользовательское, основанное на cached_db
, вам следует переопределить префикс ключа кэша, чтобы предотвратить столкновение пространств имен:
class SessionStore(CachedDBStore):
cache_key_prefix = "mysessions.custom_cached_db_backend"
# ...
Идентификаторы сессий в URL-адресах¶
Фреймворк сессий Django полностью и исключительно основан на cookie. Он не возвращается к помещению идентификаторов сессий в URL в качестве последнего средства, как это делает PHP. Это намеренное дизайнерское решение. Такое поведение не только делает URL уродливыми, но и делает ваш сайт уязвимым для кражи идентификатора сессии через заголовок «Referer».