• 3.2
  • 5.0
  • 6.1
  • Версия документации: 3.1

Как использовать сессии

Django полностью поддерживает сессии для анонимных пользователей, позволяет сохранять и получать данные для каждой посетителя сайта. Механизм сессии сохраняет данные на сервере и самостоятельно управляет сессионными куками. Куки содержат ID сессии, а не сами данные (если только вы не используете бэкенд на основе кук).

Активируем сессии

Сессии реализованы через промежуточный слой.

Чтобы активировать сессии, выполните следующие действия:

  • Убедитесь, что 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, чтобы добавить таблицу в базу данных.

Использование кэша для хранения сессии

Для улучшения производительности вы можете использовать кэш для хранения сессии.

Для этого вы должны настроить кэш, смотрите раздел о кэше.

Предупреждение

You should only use cache-based sessions if you’re using the Memcached cache backend. The local-memory cache backend doesn’t retain data long enough to be a good choice, and it’ll be faster to use file or database sessions directly instead of sending everything through the file or database cache backends. Additionally, the local-memory cache backend is NOT multi-process safe, therefore probably not a good choice for production environments.

Если вы указали несколько кэшей в CACHES, Django будет использовать кэш по умолчанию. Чтобы использовать другой кэш, укажите его название в SESSION_CACHE_ALIAS.

Once your cache is configured, you’ve got two choices for how to store data in the cache:

  • Set SESSION_ENGINE to "django.contrib.sessions.backends.cache" for a simple caching session store. Session data will be stored directly in your cache. However, session data may not be persistent: cached data can be evicted if the cache fills up or if the cache server is restarted.

  • For persistent, cached data, set SESSION_ENGINE to "django.contrib.sessions.backends.cached_db". This uses a write-through cache – every write to the cache will also be written to the database. Session reads only use the database if the data is not already in the cache.

Both session stores are quite fast, but the simple cache is faster because it disregards persistence. In most cases, the cached_db backend will be fast enough, but if you need that last bit of performance, and are willing to let session data be expunged from time to time, the cache backend is for you.

If you use the cached_db session backend, you also need to follow the configuration instructions for the using database-backed sessions.

Использование файловой системы для хранения сессии

Чтобы использовать файловую систему, укажите "django.contrib.sessions.backends.file" в SESSION_ENGINE.

Вы также можете установить параметр SESSION_FILE_PATH (который по умолчанию выводит данные из tempfile.gettempdir(), скорее всего /tmp), чтобы контролировать, где Django хранит файлы сеансов. Обязательно проверьте, есть ли у вашего веб-сервера разрешения на чтение и запись в это место.

Использование сессии в представлениях

Когда 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')

keys()
items()
setdefault()
clear()

Также содержит следующие методы:

flush()

Удаляет данные текущей сессии и сессионную куку. Можно использовать, если необходимо убедиться, что старые данные не доступны с браузера пользователя (например, функция django.contrib.auth.logout() вызывает этот метод).

Устанавливает тестовую куку, чтобы проверить, что браузер пользователя поддерживает куки. Из-за особенностей работы кук вы не сможете проверить тестовую куку, пока пользователь не запросит следующую страницу. Подробности смотрите ниже (Setting test cookies).

Returns either True or False, depending on whether the user’s browser accepted the test cookie. Due to the way cookies work, you’ll have to call set_test_cookie() on a previous, separate page request. See Setting test cookies below for more information.

Удаляет тестовую куку. Используйте, чтобы убрать за собой.

New in Django 3.0.

Returns the age of session cookies, in seconds. Defaults to SESSION_COOKIE_AGE.

set_expiry(value)

Указывает время жизни сессии. Вы можете передать различные значения:

  • Если value целое число, сессия истечет после указанного количества секунд неактивности пользователя. Например, request.session.set_expiry(300) установит время жизни равное 5 минутам.

  • If value is a datetime or timedelta object, the session will expire at that specific date/time. Note that datetime and timedelta values are only serializable if you are using the PickleSerializer.

  • Если value равно 0, срок действия файла cookie сеанса пользователя истечет при закрытии веб-браузера пользователя.

  • Если value равно None, сессия будет использовать глобальное поведение.

Чтение сессии не обновляет время жизни сессии. Время жизни просчитывается с момента последнего изменения.

get_expiry_age()

Возвращает время в секундах до окончания действия сессии. Для сессий без времени окончания (или для тех, что действуют пока работает браузер), значение будет равно SESSION_COOKIE_AGE.

Функция понимает два необязательных именованных аргумента:

  • modification: время последнего изменения сессии в виде объекта datetime. По умолчанию соответствует текущему времени.

  • expiry: expiry information for the session, as a datetime object, an int (in seconds), or None. Defaults to the value stored in the session by set_expiry(), if there is one, or None.

get_expiry_date()

Возвращает дату окончания действия сессии. Для сессий, у которых дата завершения не указана (или которые завершаются при закрытии браузера), значением будет дата, отстоящая на SESSION_COOKIE_AGE секунд от текущего момента.

This function accepts the same keyword arguments as get_expiry_age().

get_expire_at_browser_close()

Возвращает либо True, либо False, в зависимости от того, истечет ли срок действия файла cookie сеанса пользователя при закрытии веб-браузера пользователя.

clear_expired()

Удаляет просроченные сессии из хранилища сессий. Этот метод класса используется clearsessions.

cycle_key()

Создает новый ключ сессии при сохранении текущих данных в ней. Функция django.contrib.auth.login() вызывает этот метод, чтобы противодействовать фиксации сессии.

Сериализация сессии

Данная атака может быть смягчена, если выполнять сериализацию данных сессии с помощью JSON вместо pickle. Для этого в Django 1.5.3 добавили параметр SESSION_SERIALIZER, определяющий метод сериализации сессии. В целях обратной совместимости, по умолчанию параметр указывает на django.contrib.sessions.serializers.PickleSerializer в Django 1.5.x, но для укрепления безопасности, начиная с Django 1.6, установлен в django.contrib.sessions.serializers.JSONSerializer. Даже принимая во внимание предостережения из Реализация своего сериализатора, мы настоятельно рекомендуем использовать JSON сериализацию особенно при использовании кук в качестве хранилища сессии.

For example, here’s an attack scenario if you use pickle to serialize session data. If you’re using the signed cookie session backend and SECRET_KEY is known by an attacker (there isn’t an inherent vulnerability in Django that would cause it to leak), the attacker could insert a string into their session which, when unpickled, executes arbitrary code on the server. The technique for doing so is simple and easily available on the internet. Although the cookie session storage signs the cookie-stored data to prevent tampering, a SECRET_KEY leak immediately escalates to a remote code execution vulnerability.

Поставляемые сериализации

class serializers.JSONSerializer

Обёртка над JSON сериализатором из django.core.signing. Может сериализовать только простые типы данных.

In addition, as JSON supports only string keys, note that using non-string keys in request.session won’t work as expected:

>>> # 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

Supports arbitrary Python objects, but, as described above, can lead to a remote code execution vulnerability if SECRET_KEY becomes known by an attacker.

Реализация своего сериализатора

Note that unlike PickleSerializer, the JSONSerializer cannot handle arbitrary Python data types. As is often the case, there is a trade-off between convenience and security. If you wish to store more advanced data types including datetime and Decimal in JSON backed sessions, you will need to write a custom serializer (or convert such values to a JSON serializable object before storing them in request.session). While serializing these values is often straightforward (DjangoJSONEncoder may be helpful), writing a decoder that can reliably get back the same thing that you put in is more fragile. For example, you run the risk of returning a datetime that was actually a string that just happened to be in the same format chosen for datetimes).

Класс вашего сериализатора должен реализовать два метода: 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() для очистки куки после проверки. Очистку следует выполнять после окончания этапа проверки наличия поддержки кук.

Покажем типичный вариант использования:

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

An API is available to manipulate session data outside of a view:

>>> 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.

If you’re using the django.contrib.sessions.backends.db backend, each session is a normal Django model. The Session model is defined in django/contrib/sessions/models.py. Because it’s a normal model, you can access sessions using the normal Django database API:

>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)

Note that you’ll need to call get_decoded() to get the session dictionary. This is necessary because the dictionary is stored in an encoded format:

>>> 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. Это укажет 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 как было описано выше в using sessions in views.

Примечание

Some browsers (Chrome, for example) provide settings that allow users to continue browsing sessions after closing and re-opening the browser. In some cases, this can interfere with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting and prevent sessions from expiring on browser close. Please be aware of this while testing Django applications which have the SESSION_EXPIRE_AT_BROWSER_CLOSE setting enabled.

Очистка хранилища сессии

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

Для понимания проблемы давайте рассмотрим, что произойдёт с хранилищем на основе базы данных. При авторизации пользователя Django добавляет запись в таблицу django_session. Django обновляет эту запись при каждом изменении данных сессии. Если пользователь завершает сеанс явно, то Django удаляет эту запись. В противном случае эта запись не удаляется. Похожее происходит и в случае файлового хранилища.

Django не обеспечивает автоматическую очистку просроченных сессий. Следовательно, эта задача ложится на вас на регулярной основе. Django представляет для этой цели команду очистки: clearsessions. Рекомендуется выполнять эту команду на регулярной основе, например, через cron ежедневно.

Следует отметить, что хранилище на основе кэша не подтверждено этой проблеме так как кэш автоматически удаляет устаревшие данные. Так же нет проблемы и с хранилищем на основе кук, потому что они хранятся в браузерах у пользователей.

Параметры конфигурации

Несколько параметров конфигурации Django представляют вам управление поведением сессии:

Безопасность сессии

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

Например, атакующий может авторизоваться на good.example.com и получить достоверную сессию для своего аккаунта. Если у атакующего есть контроль над bad.example.com, он может использовать его для отправки своего ключа сессии вам, так как поддомену разрешено устанавливать куки для *.example.com. Когда вы посетите good.example.com, вы будете авторизован как атакующий и можете непреднамеренно внести важные персональные данные (например, номер кредитной карты) в аккаунт атакующего.

Другой возможной атакой может быть ситуация, если сайт good.example.com имеет в параметре конфигурации SESSION_COOKIE_DOMAIN значение ".example.com", что может привести отправке сессионной куки на сайт bad.example.com.

Технические детали

  • The session dictionary accepts any json serializable value when using JSONSerializer or any picklable Python object when using PickleSerializer. See the pickle module for more information.

  • Данные сессии сохраняются в базе данных в таблице django_session .

  • Django отправляем куки только когда это требуется. Если вы не сохранили никаких данных в сессию, то сессионная кука не будет отправляться.

Объект SessionStore

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

All SessionStore classes available in Django inherit from SessionBase and implement data manipulation methods, namely:

  • существует()

  • создать()

  • сохранить()

  • удалить()

  • загрузка()

  • 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)

Возвращает новый экземпляр объекта модели сеанса, который представляет текущее состояние сеанса.

Переопределение этого метода дает возможность изменять данные модели сеанса перед их сохранением в базе данных.

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 полностью, и исключительно, основана на кук. Django не помещает идентификатор сессии в URL в качестве крайнего варианта, как это делает PHP. Это - осознанное проектное решение. Мало того, что такое поведение делает URL уродливыми, оно делает ваш сайт уязвимым для воровства идентификатора сессии через заголовок «Referer».

Back to Top