Remember Me на Django
Недавно возникла необходимость сделать простую вещь – функциональность «запомнить меня». Требования простые: при установленном флажке «запомнить» пользователь считается прошедшим авторизацию и не должен вводить пароль еще раз. Выход из системы в этом случае производится при нажатии кнопки/ссылки «выйти».
При попытке найти что-то готовое результат был курьезный: кто-то решает это путем установки времени истечения сессии в 2036–2039 годах, другие примеры просто неправильные (по всей видимости, написаны под старые версии Django). Пришлось писать решение самому.
Если ваш сайт использует сессии, то механизм «запомнить меня» довольно простой: после успешного прохождения авторизации нужно, используя метод сессии set_expiry, указать, до какого момента будет действительна данная сессия. К сожалению (или к счастью), бесконечно она действительна быть не может, поэтому вы должны либо ее обновлять время от времени, либо просто поставить срок истечения, равный времени, когда вы планируете сменить работу, плюс еще пару лет, чтобы уж наверняка это стало чужой проблемой.
Я решил задачу, написав очень простой и понятный middleware:
from django.conf import settings from datetime import timedelta, date class KeepLoggedInMiddleware(object): def process_request(self, request): if not request.user.is_authenticated() or not settings.KEEP_LOGGED_KEY in request.session: return if request.session[settings.KEEP_LOGGED_KEY] != date.today(): request.session.set_expiry(timedelta(days=settings.KEEP_LOGGED_DURATION)) request.session[settings.KEEP_LOGGED_KEY] = date.today() return
Чем удобен middleware: не требуется никакой дополнительный код где-то еще. Для работы в Settings нужно сделать следующее:
- Добавить имя_модуля. KeepLoggedInMiddleware в MIDDLEWARE_CLASSES (я поставил его последним, чтобы уже работал middleware сессий)
- Добавить настройки:
# Keep me logged settings KEEP_LOGGED_KEY = 'keep_me_logged' KEEP_LOGGED_DURATION = 365 # in days
KEEP_LOGGED_KEY – имя ключа с данными в сессии пользователя, KEEP_LOGGED_DURATION – срок в днях, который вы бы хотели помнить пользователя. Одного года, на мой взгляд, более чем достаточно.
Затем в функции обработки формы авторизации, если флажок «запомнить меня» включен, вы делаете примерно следующее:
form = LoginForm(request.POST) if form.is_valid(): .... if form.remember: request.session[settings.KEEP_LOGGED_KEY] = True
После чего при обработке любого запроса middleware увидит, что настройка включена, а обновление не случилось, и обновит дату в сессии. Чаще чем раз в день, на мой взгляд, этого делать не нужно.
Можно не хранить дату последнего обновления, а просто проверять, когда истечет текущая сессия, и продлевать ее – правда, количество кода при этом не изменится, так что это дело вкуса.
При выходе из системы (logout) Django автоматически почистит все данные сессии, т. е. никаких дополнительных действий не требуется.
Ответы на: Remember Me на Django
Василий пишет:
22 февраля, 2011
не совсем понятно зачем это. в чем проблема выставлять expires самой сессии?
krvss пишет:
23 февраля, 2011
Это и происходит. Но expires надо обновлять, иначе он истечет. Либо ставить на 20 лет вперед, что мне кажется некрасивым.
Василий пишет:
23 февраля, 2011
Почему бы тогда просто не обновлять стандартную куку? Есть даже опция такая для сеттингов. Или, если уж ты хочешь обновлять не чаще, чем раз в день писать мидлвар, работающий со стандартными именами сессионных переменных?
krvss пишет:
23 февраля, 2011
Какую именно куку ты имеешь в виду, когда говоришь о стандартной? 🙂
Как я ужу писал, строки 7-8-9 можно заменить на другие, по вкусу. Главное принцип: сессия будет истекать в любом случае, и самое удобное продлять ее время от времени через middleware — так как он будет вызываться при любом обращении, то не нужно заморачиваться, добавляя дополнительный код во вью.
Я к тому, что тут упрощать здесь практически некуда дальше, и так все очень просто — можно по другому хранить обновление, но выигрыша особого не получится. Разве что убрать вообще проверку и в любом случае вызывать set_expiry. А это мне лично тоже не по сердцу.