Подсистема сеансов в Django
С учетом всех ограничений и потенциальных уязвимостей становится понятно, что cookies и сохраняемые сеансы являются примерами болевых точек веб-разработки. Но так как фреймворк Django стремится быть эффективным целителем, в него входит подсистема сеансов, предназначенная для преодоления этих трудностей.
Эта подсистема позволяет сохранять произвольные данные о каждом посетителе сайта. Данные хранятся на сервере, а сам механизм абстрагирует отправку и получение cookies. Внутри cookies передается только свертка идентификатора сеанса, а не сами данные, что позволяет защититься от большинства проблем, связанных с cookies.
Рассмотрим, как включить поддержку сеансов и использовать их в представлениях.
Включение поддержки сеансов
Сеансы реализованы с помощью дополнительных процессоров (см. главу 17) и модели Django. Чтобы включить поддержку сеансов, выполните следующие действия:
1. Убедитесь, что параметр MIDDLEWARE_CLASSES содержит строку ‘django. contrib. sessions, middleware. SessionMiddlewa re’.
2. Убедитесь, что в параметре INSTALLED_APPS присутствует приложение ‘django.contrib.sessions’ (если вам пришлось его добавить, не забудьте выполнить команду manage, ру syncdb).
В заготовке файла с параметрами, созданной командой startproject, обе строки уже присутствуют, поэтому, если вы их не удаляли, для включения сеансов делать ничего не придется.
Если вы не хотите использовать сеансы, можете удалить строку
SessionMiddleware из параметра MIDDLEWARE_CLASSES и строку ‘django. contrib.sessions’ из параметра INSTALLED_APPS. Накладные расходы при этом уменьшатся лишь чуть-чуть, но курочка по зернышку клюет.
Использование сеансов в представлениях
Если процессор SessionMiddleware активирован, то каждый объект HttpRequest (первый аргумент любой функции представления в Django) будет иметь атрибут session, аналогичный словарю. К нему можно обращаться как к обычному словарю. Например:
tt Установить значение переменной сеанса: request.session["fav_color"] = "blue"
tt Получить значение переменной сеанса - эта операция может выполняться tt и в другом представлении, и в текущем, и даже много запросов спустя. fav_color = request.session["fav_color"]
tt Удалить переменную сеанса: del request.session["fav_color"]
tt Проверить наличие переменной сеанса: if "fav_color" in request.session:
Объект request.session поддерживает и другие методы словаря, в частности keys() и itemsQ. Для эффективной работы с сеансами в Django существует ряд простых правил.
• Используйте в качестве ключей словаря request.session обычные строки Python (а не целые числа, объекты и т. д.);
• Ключи, начинающиеся со знака подчеркивания, зарезервированы Django для внутреннего использования. В действительности таких внутренних ключей совсем немного, но если вы не знаете точно, как они называются (и не готовы следить за всеми изменениями в коде Django), то лучше не употребляйте имена, начинающиеся с символа подчеркивания во избежание конфликтов с Django.
Например, не следует использовать в сеансе ключ _fav_color: request. session[‘ _fav_color’ ] = ‘blue’ tt He делайте так!
• He подменяйте request.session новым объектом, не изменяйте значения его атрибутов и не добавляйте новые атрибуты. Используйте его исключительно как словарь Python. Например:
request, session = some_other_object tt He делайте так! request, session, foo = ‘bar’ tt He делайте так!
Рассмотрим несколько примеров. В следующем простеньком представлении переменной has_commented присваивается значение True после того, как пользователь отправит свой комментарий. Это простой (но не очень надежный) способ предотвратить отправку пользователем более одного комментария:
def post_comment(request):
if request.method != ‘POST’:
raise Http404(‘Разрешены только POST-запросы’ )
if ‘comment’ not in request.POST:
raise Http404(‘Отсутствует комментарий’)
if request.session.get(‘has_commented’, False):
return HttpResponse("Вы уже отправляли комментарий.")
с = comments.Comment(comment=request.P0ST[‘comment’ ])
с.save()
request.session[‘has_commented’] = True return HttpResponse(‘Спасибо за комментарий!’)
А в этом, тоже упрощенном, представлении обрабатывается вход зарегистрированного пользователя в систему:
def login(request):
if request.method != ‘POST’:
raise Http404(‘Разрешены только POST-запросы’)
try:
m = Member.objects.get(username=request.P0ST[‘username’]) if m.password == request.P0ST[‘password’]: request.session[‘member_id’] = m.id return HttpResponseRedirect(‘/you-are-logged-in/’) except Member.DoesNotExist:
return нирЯезропэеС’Неправильное имя или пароль.")
А здесь зарегистрированный пользователь выходит из системы:
def logout(request): try:
del request.session[‘member_id’] except KeyError: pass
return HttpResponse("Bbi вышли.")
Примечание ——————————————————————————
На практике так обрабатывать вход на сайт не следует. Обсуждаемая ниже подсистема аутентификации решает эту задачу гораздо более надежно и удобно. Приведенные выше примеры намеренно упрощены, чтобы был понятен базовый механизм.
Установка проверочных cookies
Выше мы отмечали, что нельзя слепо рассчитывать на то, что броузер будет принимать cookies. Поэтому Django предлагает простой способ проверить, так ли это. Достаточно в каком-нибудь представлении вызвать метод request.session.set_test_cookie(), а в одном из последующих (не в том же самом!) - метод request.session.test_cookie_worked().
Такое, кажущееся странным, распределение обязанностей между методами set_test_cookie() и test_cookie_worked() обусловлено особенностями механизма работы cookies. Послав cookie, невозможно сказать, принял ли его броузер, пока не придет следующий запрос.
Считается хорошим тоном вызывать метод delete_test_cookie(), чтобы прибрать за собой. Делайте это после того, как получите результат проверки.
Приведем пример:
def login(request):
# Если получена форма…
if request.method == ‘POST’:
# Убедиться, что проверочный cookie был сохранен броузером
# (он устанавливается ниже):
if request.session.test_cookie_worked():
# Проверочный cookie был получен, удалить его. request.session.delete_test_cookie()
# В действующем приложении здесь следовало бы проверить
# имя пользователя и пароль, но это всего лишь пример… return HttpResponse("Bbi вошли в систему.")
# Проверочный cookie не был сохранен, выводим сообщение об
# ошибке. На действующем сайте следовало бы вывести что-нибудь
# более понятное, else:
return HttpResponse(
"Включите поддержку cookie и попробуйте еще раз.")
# Если форма еще только отправляется для заполнения, послать
# вместе с ней проверочный cookie, request.session.set_test_cookie()
return render_to_response(‘foo/login_form.html’)
Примечание ————————————————————————-
Еще раз напомним, что встроенные функции аутентификации делают все это автоматически.
Использование сеансов вне представлений
С точки зрения внутренней реализации каждый сеанс - это обычная модель Django, определение которой находится в модуле django.contrib. sessions.models. Сеанс идентифицируется более-менее случайной строкой из 32 символов, хранящейся в cookie. Поскольку сеанс - это обычная модель, то для доступа к нему можно использовать 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)
Чтобы получить данные, хранящиеся в сеансе, нужно вызвать метод get_decoded(). Это необходимо, так как данные в словаре хранятся в зашифрованном виде:
»> s.session_data
‘KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj…’
»> s.get_decoded() {‘user_id’: 42}
Когда сохраняются сеансы
По умолчанию Django сохраняет сеанс в базе данных только при его модификации, то есть после присваивания или удаления какого-нибудь ключа.
# Сеанс изменен.
request.session[‘foo’] = ‘bar’
tt Сеанс изменен.
del request.session[‘foo’]
tt Сеанс изменен, request.session[‘foo" ] = {}
tt Стоп! Сеанс HE изменен, поскольку модифицируется объект tt request. session[‘ foo’ ], а не request, session, request.session[‘foo’][1 bar’] = ‘baz’
Чтобы изменить такое поведение по умолчанию, присвойте параметру SESSION_SAVE_EVERY_REQUEST значение True. В этом случае Django будет сохранять сеанс в базе данных при каждом запросе, даже если сеанс не изменился.
Отметим, что обычно сеансовый cookie посылается только в момент создания или изменения сеанса. Если же SESSION_SAVE_EVERY_REQUEST равен True, то cookie будет посылаться при каждом запросе. И при каждом запросе будет обновляться свойство expires.
Постоянные и временные cookies
Возможно, вы обратили внимание, что cookie Google, упомянутый в начале главы, содержал значение expires=Sun, 17-Jan-2038 19:14:07 GMT;. В cookie может быть указана необязательная дата истечения срока хранения, извещающая броузер о том, когда следует удалить этот cookie. Если срок хранения не определен, то cookie будет уничтожен при закрытии окна броузера. Управлять этим аспектом поведения подсистемы сеансов позволяет параметр SESSI0N_EXPIRE_AT_BR0WSER_CL0SE.
По умолчанию параметр SESSI0N_EXPIRE_AT_BR0WSER_CL0SE имеет значение False, то есть cookie будут храниться в броузере пользователя в течение SESSI0N_C00KIE_AGE (по умолчанию две недели, то есть 1 209 600 секунд). Не изменяйте этот режим, если не хотите, чтобы пользователь был вынужден заново входить в систему при каждом открытии броузера.
Если в параметре SESSION_EXPIRE_AT_BROWSER_CLOSE установить значение True, то Django будет посылать временные cookies, хранящиеся, только пока броузер открыт.
Технические детали
Для тех, кому это интересно, сообщим кое-что о внутреннем
устройстве подсистемы сеансов.
• В словаре сеанса допускается сохранять любой объект, который может быть сериализован с помощью модуля pickle. Дополнительные сведения об этом встроенном модуле см. в документации по Python.
• Данные сеансов хранятся в таблице базы данных django_ses- sion.
• Данные сеансов извлекаются из базы по мере необходимости. Если вы не будете вызывать метод request, session, то Django не станет обращаться к таблице.
• Django посылает cookie, только когда это необходимо. Если в сеансе не сохранялось никаких данных, то сеансовый cookie не посылается (если только параметр SESSION_SAVE_EVERY_RE- QUEST не равен True).
• Вся подсистема сеансов в Django целиком и полностью основана на cookie. Если использование cookie невозможно, включение идентификатора сеанса в URL не рассматривается в качестве крайнего средства, как это происходит в некоторых других фреймворках (PHP, JSP).
Это сделано умышленно. Включение идентификатора сеанса в адрес URL не только уродует последний, но и делает сайт уязвимым для некоторых видов кражи идентификатора сеанса через заголовок Referer.
Если ваше любопытство еще не удовлетворено, загляните в исходный код модуля django.contrib.sessions; он достаточно прост.
Прочие параметры сеансов
Существует еще несколько параметров, влияющих на порядок использования cookies в сеансах Django (табл. 14.2).
Таблица 14.2. Параметры, влияющие на поведение cookie
Параметр |
Описание |
Значение по умолчанию |
SESSI0N_C00KIE_D0MAIN |
Домен, для которого действует сеансовый cookie. Для междоменных cookie значением должна быть строка, например ". example. com", а для стандартных - None. |
None |
Таблица 14.2. (Продолжение)
Параметр |
Описание |
Значение по умолчанию |
SESSI0N_C00KIE_NAME |
Имя сеансового cookie. Произвольная строка. |
"sessionid" |
SESSION_COOKIE_SECURE |
Должен ли сеансовый cookie быть защищенным. Если установлено значение True, то cookie будет посылаться только по HTTPS-соединению. |
False |
Источник: Головатый А., Каплан-Мосс Дж. Django. Подробное руководство, 2-е издание. - Пер. с англ. - СПб.: Символ- Плюс, 2010. - 560 е., ил.
Похожие посты:
- Авторитетные сайты ссылаются только на ценный материал (0)
- Ссылки даются на URL-адреса, а не на страницы (0)
- Выбор поставщика услуг по поисковой оптимизации сайта (0)
- Устранение отступа между элементом h1 и следующим за ним абзацем (0)
- Задание разных таблиц стилей для Internet Explorer 6 и 7 (0)
- Доступность и альтернативные устройства (0)
- Нужно ли отображать на сайте инструменты для изменения размера шрифта или переключения между различными таблицами стилей? (0)