Всем привет!
Аутентификация пользователей уже давно является типовой задачей. В Django, как и в любом современном вэб-фреймворке, есть отличный механизм аутентификации пользователей.
Однако, этот механизм по умолчанию использует логин в качестве идентификатора, в то время как все мы уже привыкли использовать для входа email.
Когда мне понадобилось реализовать этот функционал, оказалось что существует не так много туториалов, особенно на русском, в которых бы описывалось как сделать регистрацию по email, отправку верифицирующего письма, сброс пароля и другие, в общем то вполне обычные вещи.
Я решил исправить эту несправедливость.
Покопавшись немного в интернетах, мой выбор пал на модуль django-user-accounts.
Этот модуль — часть экосистемы Pinax, имеет неплохую документацию и, что самое приятное, немного легко-читаемого кода.
Модуль умеет:
- Регистрировать пользователя по Email;
- Отправлять письмо с подтверждающей ссылокой;
- Аутентифицировать пользователя при помощи Email и пароля;
- Изменять пароль из интерфейса;
- Сбрасывать и восстанавливать пароль;
- Отслеживать "протухание" пароля;
- Изменять параметры аккаунта (например локаль или часовой пояс);
- Удалять аккаунт.
Инсталяция
Для установки выполняем команду:
pip install django-user-accounts
Не забываем добавить зависимость в файл requirements.txt:
django-user-accounts==2.0.3
В settings.py добавляем INSTALLED_APPS:
INSTALLED_APPS =[
.....
'django.contrib.sites',
'account'
]
Важно добавить стандартный джанговский модуль sites, так как account от него зависит.
Дальше добавляем MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = [
.....
'account.middleware.LocaleMiddleware',
'account.middleware.TimezoneMiddleware'
]
И context_processors:
TEMPLATES = [
{
.....
'OPTIONS': {
'context_processors': [
.....
'account.context_processors.account'
],
},
},
]
Для того, чтобы у нас email был уникальный и чтобы требовалось подтвеждение добавим два ключа в settings.py:
ACCOUNT_EMAIL_UNIQUE = True
ACCOUNT_EMAIL_CONFIRMATION_REQUIRED = True
Теперь можно выполнить миграции:
python manage.py migrate
В результате в БД появятся новые таблицы:
account_account
account_accountdeletion
account_emailaddress
account_emailconfirmation
account_passwordexpiry
account_passwordhistory
account_signupcode
account_signupcoderesult
django_site
Если у вас ранее не было подключенного модуля sites, то нужно создать сайт:
python manage.py shell
>>> from django.contrib.sites.models import Site
>>> site = Site(domain='localhost:8000', name='localhost:8000')
>>> site.save()
>>> site.id
2
И добавить в setting.py id нового сайта:
SITE_ID = 2
Все templates для необходимых страниц и текстов писем можно скачать из pinax-theme-bootstrap и просто поместить их по адресу yourproject/yourapp/templates/account.
Настройка
Если вы собираетесь подключать этот модуль на уровне Django-приложения, а не проекта, то для корректной работы маршрутизаций, добавим в settings.py следующие строки:
ACCOUNT_LOGIN_URL = 'yourapp:account_login'
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = ACCOUNT_LOGIN_URL
ACCOUNT_PASSWORD_RESET_REDIRECT_URL = ACCOUNT_LOGIN_URL
ACCOUNT_EMAIL_CONFIRMATION_URL = "yourapp:account_confirm_email"
ACCOUNT_SETTINGS_REDIRECT_URL = 'yourapp:account_settings'
ACCOUNT_PASSWORD_CHANGE_REDIRECT_URL = "yourapp:account_password"
И добавим урлы в файл yourapp/urls.py:
urlpatterns = [
.....
url(r"^account/", include("account.urls")),
.....
]
Теперь доступны следующие адреса:
account/signup/
account/login/
account/logout/
account/confirm_email/<key>
account/password/
account/password/reset/
account/password/reset/<token>
account/settings/
account/delete/
Осталось лишь добавить настройки для почтового сервера в settings.py:
DEFAULT_FROM_EMAIL = 'support@yoursite.ru'
EMAIL_HOST = "smtp.yoursmtpserver.ru"
EMAIL_PORT = 25
EMAIL_HOST_USER = "user"
EMAIL_HOST_PASSWORD = "pass"
Если вы всё правильно сделали, то по указанным урлам можно логиниться при помощи логина/пароля, а при создании учетки вам на почту будет отправлено письмо для подтверждения email-а.
Лирическое отступление
Была обнаружена бага, которая не позволяет корректно сформировать ссылку на восстановление пароля если вы добавили ссылки в приложение, а не в проект.
Пока не принят пулл-реквест можно вбить костыль в файл yourproject/urls.py
from account.views import PasswordResetTokenView
urlpatterns = [
.....
url(r"^account/password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$", PasswordResetTokenView.as_view(), name="account_password_reset_token"),
.....
]
В пулл-реквесте появится новая настройка:
ACCOUNT_PASSWORD_RESET_TOKEN_URL = 'yourapp:account_password_reset_token'
Аутентификация по Email
Для начала добавляем соответсвующий backend в settings.py:
AUTHENTICATION_BACKENDS = [
'account.auth_backends.EmailAuthenticationBackend',
]
Затем удаляем из формы регистрации username, для этого в файл yourapp/forms.py добавляем следующее:
import account.forms
class SignupForm(account.forms.SignupForm):
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
del self.fields["username"]
А в файл yourapp/views.py это:
import yourapp.forms
import account.forms
import account.views
class LoginView(account.views.LoginView):
form_class = account.forms.LoginEmailForm
class SignupView(account.views.SignupView):
form_class = yourapp.forms.SignupForm
def generate_username(self, form):
username = form.cleaned_data["email"]
return username
def after_signup(self, form):
# do something
super(SignupView, self).after_signup(form)
Здесь мы для вьюшек задаем классы форм: LoginEmailForm и нашу SignupForm соответсвенно, а так же переопределяем метод after_signup, чтобы можно было подмешать в него какое-нибудь нужное нам поведение. По-умолчанию полю username присваивам значение email.
Осталось лишь переопределить наши url-ы в файле yourapp/urls.py:
from . import views
urlpatterns = [
.....
url(r"^account/login/$", views.LoginView.as_view(), name="account_login"),
url(r"^account/signup/$", views.SignupView.as_view(), name="account_signup"),
url(r"^account/", include("account.urls")),
.....
]
Обращаю внимание, что вызов кастомных вьюшек должен идти перед подключением account.urls, иначе они не переопределятся.
Миграция старых данных
Для того чтобы текущие пользователи могли залогиниться нужно добавить их email адреса в таблицу account_emailaddress:
insert into account_emailaddress(email, verified, "primary", user_id)
select au.email, True, True, au.id
from auth_user au
where au.email is not null
В данном случае в поле verified вставляется значение true, т.е. мы сразу их подтверждаем.
Для того, чтобы у всех пользователей появился аккаунт, заполнить таблицу account_account:
insert into account_account (timezone, "language", user_id)
select '','ru', id
from auth_user
Заключение
Подробнее о модуле django-user-account вы можете узнать в официальной документации.
Исходный код находится тут. Его полезно почитать, чтобы чуть лучше разобраться в том, как работает механизм auth в Django.
Надеюсь, эта статья поможет вам сэкономить время. Просьба поделиться в комментариях, какими инструментами пользуетесь вы.
Комментарии (7)
Mogost
12.11.2017 09:40Именно в аспекте аутентификации для django есть ещё один хороший пакет, который можно использовать — django-allauth. Так по пунктам:
- Регистрировать пользователя по Email;
возможна через ACCOUNT_AUTHENTICATION_METHOD='email'. - Отправлять письмо с подтверждающей ссылкой;
возможно из коробки (ACCOUNT_EMAIL_VERIFICATION='mandatory'), при том там очень гибкие настройки. - Аутентифицировать пользователя при помощи Email и пароля;
возможно из коробки. - Изменять пароль из интерфейса;
allauth.account.views.PasswordChangeView присутствует. - Сбрасывать и восстанавливать пароль;
allauth.account.views.PasswordResetView присутствует. - Отслеживать «протухание» пароля;
тут придется дописать кода, но не выглядит ничем сложным. - Изменять параметры аккаунта (например локаль или часовой пояс);
к email аутентификации не относится никак, нужны дополнительные поля к стандартной юзер модели, наследуетесь и определяет их. - Удалять аккаунт.
тут тоже код дописывать придется (если надо инициировать удаление на стороне пользователя, а если со стороны админа, то в стандартной django-admin).
Плюс ко всему вы получите простой способ прикрутить аутентификацию грубо говоря через что угодно.
Документация django-allauth.readthedocs.io/en/latest
P.S. Инструмент надо выбирать себе с умом, смотреть какие доступны и какие удовлетворяют вашим задач. Конечно желательно чтоб инструмент развивался и обновлялся, всё-таки аутентификация довольно критичная часть системы.
P.S.2 djangopackages.org — хороший сайт чтоб найти инструмент именно под django.
P.S.3 Есть очень хорошая книга «Two Scoops of Django» Best Practices for Django, книга очень продуманная, обновляется с выходами новых версий Django и рассказывает о разных инструментах (в частности про django-allauth).lepism Автор
12.11.2017 10:00Спасибо большое за ценную информацию!
Я django-allauth тоже смотрел, но мне он показался слишком громоздким, по крайней мере для моих задач. Возможно в другом проекте я буду использовать именно его.
- Регистрировать пользователя по Email;
asmrnv777
Спасибо!
А что посоветуете для периодических тасков? Celery себя полностью дискредитировал тем, что попросту виснет раз в несколько дней, причем в логах ничего не пишет. И это в разных проектах на разных машинах.
Крон не особо подходит, а свой велосипед пилить не хочется.
random1st
что используете в качестве брокера?
asmrnv777
Пробовал redis и RabbitMQ, в обоих одна и та же проблема.
Mogost
Celery отличный инструмент. Если cron'a не хватает, можно попробовать решить задачу через uWSGI. Там есть cron-подобный вариант. Также есть spooler.
При том можно очень удобно все организовать с помощью декораторов.
Devroman
Celery довольно неплох, но может быть у вас есть какие-то проблемы с окружением. Однако попробуйте еще посмотреть в сторону rq (http://python-rq.org/) может покажется более удобным :)