Как использовать сессии
Django предоставляет полную поддержку анонимных сессий. Система сессий позволяет хранить и извлекать произвольные данные для каждого посетителя сайта. Данные хранятся на стороне сервера, а отправка и получение файлов cookie абстрагируются. Файлы cookie содержат идентификатор сессии, а не сами данные (если вы не используете хранилище сессий на основе cookie).
Включение сессий
Сессии реализуются с помощью части средств промежуточного слоя.
Для включения функциональности сессий выполните следующие действия:
- Измените настройку
MIDDLEWARE_CLASSESи убедитесь, что она содержит'django.contrib.sessions.middleware.SessionMiddleware'. По умолчаниюsettings.pyсозданныйdjango-admin startprojectимеетSessionMiddlewareвключёнными.
Если вы не хотите использовать сессии, можете удалить строку SessionMiddleware из MIDDLEWARE_CLASSES и 'django.contrib.sessions' из вашей INSTALLED_APPS. Это сэкономит немного ресурсов.
Настройка движка сессий
По умолчанию Django хранит сессии в базе данных (используя модель django.contrib.sessions.models.Session). Хотя это удобно, в некоторых случаях хранение данных сессий в другом месте может быть быстрее, поэтому Django можно настроить на хранение данных сессий на файловой системе или в кэше.
Использование сессий с поддержкой базы данных
Если вы хотите использовать сессии с поддержкой базы данных, вам нужно добавить 'django.contrib.sessions' в вашу настройку INSTALLED_APPS.
После настройки вашей установки запустите manage.py migrate для установки единственной таблицы базы данных, которая хранит данные сессий.
Использование кэшированных сессий
Для повышения производительности вы можете использовать хранилище сессий на основе кэша.
Чтобы хранить данные сессий с помощью системы кэширования Django, сначала необходимо убедиться, что вы настроили свой кэш. Подробную информацию см. в документации по кэшу.
Предупреждение
Вы должны использовать сессии на основе кэша только если вы используете кэш-бекенд Memcached. Кэш локальной памяти не сохраняет данные достаточно долго, чтобы быть хорошим выбором, и быстрее будет использовать файловые или базовые сессии напрямую, а не отправлять все через файловые или базовые кэш-бекенды. Кроме того, бекенд локальной памяти не является безопасным для нескольких процессов, поэтому, вероятно, не подходит для производственных сред.
Если в CACHES определено несколько кэшей, Django будет использовать кэш по умолчанию. Чтобы использовать другой кэш, установите SESSION_CACHE_ALIAS в имя этого кэша.
После настройки кэша у вас есть два варианта хранения данных в кэше:
- Установите
SESSION_ENGINEв"django.contrib.sessions.backends.cache"для простого хранилища сессий кэша. Данные сессии будут храниться непосредственно в вашем кэше. Однако данные сессии могут быть не постоянными: данные кэша могут быть удалены, если кэш заполнится или если сервер кэша перезагрузится. - Для постоянных данных кэша установите
SESSION_ENGINEв"django.contrib.sessions.backends.cached_db". Это использует кэш с прописанием через — каждое запись в кэш также записывается в базу данных. Чтение сессии использует только базу данных, если данные ещё не находятся в кэше.
Оба хранилища сессий достаточно быстрые, но простой кэш быстрее, потому что он игнорирует постоянство. В большинстве случаев бекенд cached_db будет достаточно быстрым, но если вам нужна максимальная производительность, и вы готовы к тому, что данные сессий будут удалены время от времени, бекенд cache для вас.
Если вы используете бекенд сессий cached_db, вам также нужно следовать инструкциям по настройке для использования сессий с поддержкой базы данных.
До версии 1.7 бекенд cached_db всегда использовал кэш default вместо SESSION_CACHE_ALIAS.
Использование сессий на основе файлов
Чтобы использовать сессии на основе файлов, установите настройку SESSION_ENGINE в "django.contrib.sessions.backends.file".
Вы также можете установить настройку SESSION_FILE_PATH (которая по умолчанию равна результату tempfile.gettempdir(), скорее всего, /tmp), чтобы контролировать, где Django хранит файлы сессий. Убедитесь, что ваш веб-сервер имеет права чтения и записи в это местоположение.
Использование сессий на основе файлов cookie
Примечание
Рекомендуется оставить настройку SESSION_COOKIE_HTTPONLY на True для предотвращения доступа к сохранённым данным из JavaScript.
Предупреждение
Если ключ SECRET_KEY не является секретным, и вы используете PickleSerializer, это может привести к произвольному удаленному выполнению кода.
Злоумышленник, владеющий ключом SECRET_KEY, может не только генерировать поддельные данные сессии, которым доверяет ваш сайт, но и удалённо выполнять произвольный код, так как данные сериализуются с помощью pickle.
Если вы используете сессии на основе файлов cookie, тщательно следите за тем, чтобы ваш секретный ключ всегда хранился в секрете для любого удалённо доступного приложения.
Данные сессии подписываются, но не шифруются
При использовании бекенда cookie данные сессии могут быть прочитаны клиентом.
Для защиты данных от изменений со стороны клиента используется MAC (код аутентификации сообщения), поэтому данные сессии будут недействительными при их подделке. Такая же проверка происходит, если клиент, хранящий файлы cookie (например, браузер вашего пользователя), не может сохранить все файлы cookie сессии и теряет данные. Несмотря на то, что Django сжимает данные, вполне возможно превысить обычное ограничение в 4096 байт на файл cookie.
Гарантия свежести отсутствует
Обратите также внимание, что, хотя MAC может гарантировать подлинность данных (что они были сгенерированы вашим сайтом, а не кем-то другим) и целостность данных (что они все там и правильные), он не может гарантировать свежесть, т.е. что вам отправляется последнее отправленное вам клиенту. Это означает, что для некоторых применений данных сессии бекенд файлов cookie может открыть вас для атак повторного воспроизведения. В отличие от других бекендов сессий, которые сохраняют запись на сервере для каждой сессии и делают её недействительной, когда пользователь выходит из системы, сессии на основе файлов cookie не становятся недействительными при выходе пользователя. Таким образом, если злоумышленник украдёт файлы cookie пользователя, он может использовать эти файлы cookie для входа как пользователь, даже если пользователь выйдет из системы. Файлы cookie будут считаться «просроченными» только если они старше, чем ваши 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) -
Пример:
fav_color = request.session.pop('fav_color')
-
keys()
-
items()
-
setdefault()
-
clear()
Также у него есть следующие методы:
-
flush() -
Удаляет текущие данные сессии из хранилища сессии и удаляет cookie сессии. Это используется, если необходимо убедиться, что предыдущие данные сессии больше недоступны в браузере пользователя (например, функция
django.contrib.auth.logout()вызывает её).Удаление cookie сессии — это поведение, новое в Django 1.8. Ранее, поведение заключалось в перегенерации значения ключа сессии, которое отправлялось обратно пользователю в cookie.
-
Устанавливает тестовый cookie для определения, поддерживает ли браузер пользователя cookies. Из-за работы cookies, вы не сможете протестировать это до следующего запроса страницы пользователем. Подробнее см. Настройка тестовых cookies ниже.
-
Возвращает либо
TrueилиFalse, в зависимости от того, принял ли браузер пользователя тестовый cookie. Из-за работы cookies, вы должны будете вызватьset_test_cookie()в предыдущем, отдельном запросе страницы. Подробнее см. Настройка тестовых cookies ниже.
-
Удаляет тестовый cookie. Используйте это для очистки после себя.
-
set_expiry(value) -
Устанавливает время истечения срока действия сессии. Вы можете передать различные значения:
- Если
value— целое число, сессия истечет через указанное количество секунд бездействия. Например, вызовrequest.session.set_expiry(300)сделает сессию истекшей через 5 минут. - Если
value— объектdatetimeилиtimedelta, сессия истечет в указанную дату/время. Обратите внимание, что значенияdatetimeиtimedeltaсериализуемы только при использованииPickleSerializer. - Если
valueравно0, cookie сессии пользователя истечет при закрытии веб-браузера пользователя. - Если
valueравноNone, сессия возвращается к использованию глобальной политики истечения срока действия сессии.
Чтение сессии не считается деятельностью для целей истечения срока действия. Истечение срока действия сессии вычисляется с момента последнего изменения сессии.
- Если
-
get_expiry_age() -
Возвращает количество секунд до истечения срока действия этой сессии. Для сессий без пользовательской настройки истечения срока действия (или тех, которые настроены на истечение при закрытии браузера), это будет равно
SESSION_COOKIE_AGE.Эта функция принимает два необязательных ключевых аргумента:
-
modification: последнее изменение сессии, как объектdatetime. По умолчанию — текущее время. -
expiry: информация об истечении срока действия сессии, как объектdatetime, целое число (в секундах) илиNone. По умолчанию — значение, сохраненное в сессии методомset_expiry(), если оно есть, илиNone.
-
-
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()вызывает этот метод, чтобы смягчить уязвимость «фиксации сессии».
-
Сериализация сессий
До версии 1.6, Django по умолчанию использовал pickle для сериализации данных сессии перед их сохранением в хранилище. Если вы используете хранилище сессий с подписанными cookie и SECRET_KEY известен злоумышленнику (не существует внутренней уязвимости в Django, которая бы привела к его утечке), злоумышленник мог бы вставить строку в свою сессию, которая, при разборке, выполнит произвольный код на сервере. Техника для этого проста и легко доступна в интернете. Хотя хранилище cookie сессии подписывает данные, хранящиеся в cookie, чтобы предотвратить вмешательство, утечка SECRET_KEY немедленно приводит к уязвимости удаленного выполнения кода.
Эту атаку можно предотвратить, сериализуя данные сессии с помощью JSON вместо pickle. Для этого Django 1.5.3 ввел новую настройку SESSION_SERIALIZER для настройки формата сериализации сессии. Для обратной совместимости эта настройка по умолчанию использует django.contrib.sessions.serializers.PickleSerializer в Django 1.5.x, но для повышения безопасности по умолчанию использует django.contrib.sessions.serializers.JSONSerializer в Django 1.6. Даже с оговорками, описанными в разделе Создание собственного сериализатора, мы настоятельно рекомендуем использовать сериализацию JSON, особенно если вы используете хранилище cookie.
Встроенные сериализаторы
-
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.
-
class serializers.PickleSerializer -
Поддерживает произвольные объекты Python, но, как описано выше, может привести к уязвимости удалённого кода выполнения, если
SECRET_KEYстанет известна злоумышленнику.
Напишите свой сериализатор
Обратите внимание, что в отличие от PickleSerializer, JSONSerializer не может обрабатывать произвольные типы данных Python. Как часто бывает, существует компромисс между удобством и безопасностью. Если вы хотите хранить более сложные типы данных, включая datetime и Decimal в сессиях на основе JSON, вам потребуется написать пользовательский сериализатор (или преобразовать такие значения в сериализуемый JSON-объект перед их сохранением в request.session). Хотя сериализация этих значений достаточно проста (django.core.serializers.json.DateTimeAwareJSONEncoder может быть полезным), написание декодера, который надёжно восстановит то же самое, что вы ввели, более уязвимо. Например, вы рискуете вернуть datetime, который на самом деле был строкой, которая просто имела тот же формат, который был выбран для datetime.
Ваш класс сериализатора должен реализовать два метода, 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.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() реализацию.
Установка тестовых куки
Для удобства Django предоставляет простой способ проверить, принимает ли браузер пользователя куки. Просто вызовите метод set_test_cookie() объекта request.session в представлении и вызовите test_cookie_worked() в последующем представлении – не в том же вызове представления.
Это неуклюжее разделение между set_test_cookie() и test_cookie_worked() необходимо из-за того, как работают куки. Когда вы устанавливаете куки, вы не можете узнать, принял ли браузер их, пока браузер не отправит следующий запрос.
Хорошей практикой является использование delete_test_cookie() для очистки после себя. Сделайте это после того, как вы убедились, что тестовый куки сработал.
Вот типичный пример использования:
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_to_response('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.save() >>> s.session_key '2b1189a188b44ad18c35e113ac6ceead' >>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead') >>> s['last_login'] 1376587691
Если вы используете бэкенд 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 будет сохранять сессию в базе данных при каждом запросе.
Обратите внимание, что куки сессии отправляются только при создании или изменении сессии. Если SESSION_SAVE_EVERY_REQUEST равно True, куки сессии будут отправляться при каждом запросе.
Аналогично, часть expires куки сессии обновляется каждый раз, когда отправляется куки сессии.
Сессия не сохраняется, если код состояния ответа равен 500.
Сессии длительностью работы браузера против постоянных сессий
Вы можете контролировать, использует ли фреймворк сессий сессии длительностью работы браузера или постоянные сессии, с помощью параметра SESSION_EXPIRE_AT_BROWSER_CLOSE.
По умолчанию SESSION_EXPIRE_AT_BROWSER_CLOSE установлен в False, что означает, что куки сессии будут храниться в браузерах пользователей до тех пор, пока не истечёт SESSION_COOKIE_AGE. Используйте это, если вы не хотите, чтобы пользователи входили в систему каждый раз при открытии браузера.
Если SESSION_EXPIRE_AT_BROWSER_CLOSE установлен в True, Django будет использовать куки длительностью работы браузера — куки, которые истекают, как только пользователь закрывает браузер. Используйте это, если вы хотите, чтобы пользователи входили в систему каждый раз при открытии браузера.
Этот параметр является глобальным значением по умолчанию и может быть переопределён на уровне каждой сессии путём явного вызова метода set_expiry() объекта request.session, как описано выше в разделе использование сессий в представлениях.
Примечание
Некоторые браузеры (например, Chrome) предоставляют настройки, которые позволяют пользователям продолжать сессии просмотра после закрытия и повторного открытия браузера. В некоторых случаях это может повлиять на настройку SESSION_EXPIRE_AT_BROWSER_CLOSE и предотвратить истечение сессий при закрытии браузера. Обратите внимание на это во время тестирования приложений Django, у которых включена настройка SESSION_EXPIRE_AT_BROWSER_CLOSE.
Очистка хранилища сессий
По мере создания новых сессий на вашем веб-сайте данные сессии могут накапливаться в хранилище сессий. Если вы используете бэкенд базы данных, таблица django_session будет расти. Если вы используете файловый бэкенд, в вашей временной директории будет увеличиваться количество файлов.
Чтобы понять эту проблему, рассмотрим, что происходит с бэкендом базы данных. Когда пользователь входит в систему, Django добавляет строку в таблицу базы данных django_session. Django обновляет эту строку каждый раз, когда изменяются данные сессии. Если пользователь выходит из системы вручную, Django удаляет строку. Но если пользователь не выходит из системы, строка никогда не удаляется. Аналогичный процесс происходит с файловым бэкендом.
Django не предоставляет автоматическое удаление истекших сессий. Поэтому вам необходимо регулярно очищать истекшие сессии. Django предоставляет команду управления очисткой для этой цели: clearsessions. Рекомендуется вызывать эту команду регулярно, например, как ежедневную задачу cron.
Обратите внимание, что бэкенд кэша не подвержен этой проблеме, так как кэши автоматически удаляют устаревшие данные. Точно так же и бэкенд куки, так как данные сессии хранятся в браузерах пользователей.
Параметры
Несколько параметров Django позволяют управлять поведением сессий:
SESSION_CACHE_ALIASSESSION_COOKIE_AGESESSION_COOKIE_DOMAINSESSION_COOKIE_HTTPONLYSESSION_COOKIE_NAMESESSION_COOKIE_PATHSESSION_COOKIE_SECURESESSION_ENGINESESSION_EXPIRE_AT_BROWSER_CLOSESESSION_FILE_PATHSESSION_SAVE_EVERY_REQUEST
Безопасность сессий
Поддомены на сайте могут устанавливать куки на клиенте для всего домена. Это делает возможной фиксацию сессий, если куки разрешены для поддоменов, не контролируемых доверенными пользователями.
Например, злоумышленник может войти в good.example.com и получить действительную сессию для своей учетной записи. Если злоумышленник контролирует bad.example.com, он может использовать его для отправки своего ключа сессии вам, так как поддомен может устанавливать куки на *.example.com. Когда вы посещаете good.example.com, вы войдёте в систему как злоумышленник и, возможно, непреднамеренно введёте свои конфиденциальные данные (например, информацию о кредитной карте) в учётную запись злоумышленника.
Ещё одна возможная атака — если good.example.com установит свой SESSION_COOKIE_DOMAIN на ".example.com", что приведет к отправке куки сессии с этого сайта на bad.example.com.
Технические детали
- Словарь сессий принимает любое
jsonсериализуемое значение при использованииJSONSerializerили любой пиклируемый объект Python при использованииPickleSerializer. Подробнее см. в модулеpickle. - Данные сессии хранятся в таблице базы данных с именем
django_session. - Django отправляет куки только если это необходимо. Если вы не устанавливаете данные сессии, куки сессии не отправляются.
Идентификаторы сессий в URL-адресах
Фреймворк сессий Django полностью и исключительно основан на куках. Он не использует для этого URL-адреса как последний вариант, в отличие от PHP. Это осознанное конструкторское решение. Такое поведение не только ухудшает внешний вид URL-адресов, но и делает ваш сайт уязвимым для кражи идентификаторов сессий через заголовок «Referer».
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/http/sessions/