Привет, Хабр!
Аутентификация является фундаментальной частью любого веб-приложения. Мы рассмотрим различные способы реализации аутентификации в Django, начиная от стандартных методов и заканчивая более крутыми техниками, например как 2FA и OAuth2.
Стандартная аутентификация Django
Начнём с классики. Django поставляется со встроенной системой аутентификации, которая покрывает большинство базовых потребностей. Она включает в себя модели, формы, представления и шаблоны для работы с пользователями.
Первым делом убедимся, что в нашем проекте подключены все необходимые приложения и middleware для работы системы аутентификации.
settings.py
INSTALLED_APPS = [ # ... 'django.contrib.auth', # Приложение аутентификации 'django.contrib.contenttypes',# Контент-тайпы для моделей # ... ] MIDDLEWARE = [ # ... 'django.contrib.sessions.middleware.SessionMiddleware', # Управление сессиями 'django.contrib.auth.middleware.AuthenticationMiddleware',# Аутентификация # ... ]
Эти настройки необходимы для корректной работы системы аутентификации. Middleware SessionMiddleware
и AuthenticationMiddleware
позволяют управлять сессиями и обработку аутентификации на уровне запросов.
Создание пользовательских форм
Django имеет готовые формы для регистрации UserCreationForm
и входа AuthenticationForm
, но часто возникает необходимость добавить дополнительные поля или изменить логику валидации. В таких случаях можно создать свои формы, наследуясь от стандартных.
forms.py
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.contrib.auth.models import User from django import forms class SignUpForm(UserCreationForm): email = forms.EmailField(max_length=254, help_text='Обязательное поле. Введите действующий email.') class Meta: model = User fields = ('username', 'email', 'password1', 'password2') class LoginForm(AuthenticationForm): username = forms.CharField(label='Имя пользователя') password = forms.CharField(label='Пароль', widget=forms.PasswordInput)
В SignUpForm
мы добавили поле email
, сделав его обязательным. В Meta
классе указали, какие поля должны отображаться в форме. LoginForm
переопределяет стандартную форму входа, позволяя нам изменять метки полей и виджеты.
Создание представлений для регистрации и входа
Теперь создадим представления, которые будут обрабатывать запросы на регистрацию и вход пользователей.
views.py
from django.shortcuts import render, redirect from django.contrib.auth import login, authenticate from .forms import SignUpForm, LoginForm def signup_view(request): if request.method == 'POST': form = SignUpForm(request.POST) if form.is_valid(): user = form.save() # Сохраняем нового пользователя login(request, user) # Выполняем вход return redirect('home') # Перенаправляем на главную страницу else: form = SignUpForm() return render(request, 'signup.html', {'form': form}) def login_view(request): form = LoginForm(data=request.POST or None) if request.method == 'POST': if form.is_valid(): username = form.cleaned_data['username'] password = form.cleaned_data['password'] user = authenticate(username=username, password=password) # Проверяем учетные данные if user is not None: login(request, user) # Выполняем вход return redirect('home') # Перенаправляем на главную страницу return render(request, 'login.html', {'form': form})
В signup_view
мы обрабатываем POST-запросы с данными формы регистрации. Если данные валидны, сохраняем пользователя и выполняем вход с помощью функции login
. В login_view
обрабатываем форму входа, аутентифицируем пользователя с помощью функции authenticate
и, если он существует, выполняем вход.
Настройка URL-маршрутов
Не забудем настроить URL-маршруты для наших представлений.
urls.py
from django.urls import path from .views import signup_view, login_view urlpatterns = [ path('signup/', signup_view, name='signup'), path('login/', login_view, name='login'), # ... ]
Теперь страницы регистрации и входа доступны по URL-адресам /signup/
и /login/
соответственно.
Кастомизация модели пользователя
Стандартная модель пользователя Django User
может быть недостаточной для некоторых проектов, особенно если требуется хранить дополнительные данные о пользователях. В таких случаях рекомендуется создать свою пользовательскую модель, наследуясь от AbstractUser
или AbstractBaseUser
.
Создание пользовательской модели
models.py
from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): # Добавляем дополнительные поля phone_number = models.CharField(max_length=15, blank=True, null=True) def __str__(self): return self.username
Здесь создаём класс CustomUser
, наследующийся от AbstractUser
, и добавляем новое поле phone_number
для хранения номера телефона пользователя.
Настройка проекта для использования новой модели
Чтобы Django использовал нашу кастомную модель пользователя, нужно указать её в настройках проекта.
settings.py
AUTH_USER_MODEL = 'your_app_name.CustomUser'
После этого нужно создать и применить миграции для новой модели:
python manage.py makemigrations python manage.py migrate
Обновление форм и представлений
Так как мы изменили модель пользователя, нужно обновить формы и представления, чтобы они работали с новой моделью.
forms.py
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from .models import CustomUser from django import forms class SignUpForm(UserCreationForm): email = forms.EmailField(max_length=254, help_text='Обязательное поле. Введите действующий email.') phone_number = forms.CharField(max_length=15, required=False, help_text='Необязательное поле.') class Meta: model = CustomUser fields = ('username', 'email', 'phone_number', 'password1', 'password2')
Мы заменили модель User
на CustomUser
и добавили поле phone_number
в форму регистрации.
Использование токенов для аутентификации
Если вы разрабатываете API, то токен-авторизация — лучший выбор для обеспечения безопасности и управления доступом. Она позволяет клиентским приложениям аутентифицироваться без использования сессий.
Установим Django REST Framework
Для реализации токен-авторизации установим Django REST Framework и соответствующий пакет для токенов.
pip install djangorestframework djangorestframework.authtoken
Настроим проект
Добавим необходимые приложения в настройки проекта.
settings.py
INSTALLED_APPS = [ # ... 'rest_framework', 'rest_framework.authtoken', # ... ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', # Аутентификация по токену ], }
Генерация токена для пользователя
Чтобы пользователь мог аутентифицироваться по токену, необходимо сгенерировать для него токен.
Пример генерации токена
from rest_framework.authtoken.models import Token from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.get(username='example_user') token, created = Token.objects.get_or_create(user=user) print(token.key)
Этот код можно выполнить в консоли Django python manage.py shell
или включить в скрипт. Он получает пользователя с именем example_user
и создаёт или получает токен, связанный с этим пользователем.
Использование токена в запросах
При отправке запросов к API необходимо добавлять токен в заголовок HTTP-запроса:
Authorization: Token your_token_key
Где your_token_key
— это значение токена, сгенерированного ранее.
Настройка эндпоинта для получения токена
Чтобы пользователи могли получать токены, можно настроить специальный эндпоинт.
urls.py
from django.urls import path from rest_framework.authtoken.views import obtain_auth_token urlpatterns = [ # ... path('api-token-auth/', obtain_auth_token, name='api_token_auth'), # ... ]
Теперь пользователь может получить токен, отправив POST-запрос с username
и password
на /api-token-auth/
.
OAuth2 аутентификация с django-allauth
OAuth2 позволяет пользователям входить в приложение через аккаунты в социальных сетях, например через тот же Google.
Установим пакет django-allauth
, который предоставляет готовые решения для аутентификации через социальные сети.
pip install django-allauth
Настройка проекта
Добавим необходимые приложения и настройки.
settings.py
INSTALLED_APPS = [ # ... 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', # Добавляем провайдеров социальных сетей 'allauth.socialaccount.providers.google', # ... ] AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', ) SITE_ID = 1 # Дополнительные настройки allauth ACCOUNT_EMAIL_VERIFICATION = 'none' # Не требовать верификацию email ACCOUNT_AUTHENTICATION_METHOD = 'username'# Метод аутентификации ACCOUNT_EMAIL_REQUIRED = True # Требовать email
Настройка URL-маршрутов
Добавим URL-адреса для обработки запросов allauth.
urls.py
from django.urls import path, include urlpatterns = [ # ... path('accounts/', include('allauth.urls')), # ... ]
После миграций и создания суперпользователя, можно настроить приложения социальных сетей в админке Django. Пеереходим в раздел Social applications и добавляем новое приложение, указав необходимые параметры, такие как Client ID
и Client Secret
, которые можно получить в соответствующих консольных разработчиков.
Двухфакторная аутентификация
Для повышения безопасности вашего Django-приложения можно добавить двухфакторную аутентификацию, которая требует дополнительного подтверждения при входе в аккаунт.
Установим пакет django-two-factor-auth
, который предоставляет готовое решение для реализации 2FA в Django.
pip install django-two-factor-auth
Добавим необходимые приложения и middleware в настройки вашего проекта.
settings.py
INSTALLED_APPS = [ # ... 'django_otp', # Основное приложение для работы с OTP 'django_otp.plugins.otp_static', # Поддержка статических кодов 'django_otp.plugins.otp_totp', # Поддержка TOTP (Time-based One-Time Password) 'two_factor', # Приложение двухфакторной аутентификации 'crispy_forms', # Для красивых форм 'crispy_bootstrap5', # Для использования Bootstrap 5 # ... ] MIDDLEWARE = [ # ... 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django_otp.middleware.OTPMiddleware', # Middleware для обработки OTP # ... ] # Настройки для crispy_forms CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' CRISPY_TEMPLATE_PACK = 'bootstrap5' # Настройки для шаблонов TEMPLATES = [ { # ... 'OPTIONS': { 'context_processors': [ # ... 'django.template.context_processors.request', # Необходимо для двухфакторной аутентификации # ... ], }, }, ]
Убедитесь, что django.template.context_processors.request
добавлен в context_processors
. Это необходимо для корректной работы шаблонов django-two-factor-auth
.
Добавим маршруты для двухфакторной аутентификации в ваш файл urls.py
.
urls.py
from django.contrib import admin from django.urls import path, include from two_factor.urls import urlpatterns as tf_urls urlpatterns = [ path('admin/', admin.site.urls), path('', include(tf_urls)), # Маршруты для двухфакторной аутентификации # ... ]
Пакет django-two-factor-auth
использует шаблоны для отображения форм и сообщений пользователю. По умолчанию он использует стандартные шаблоны Django. Если нужно использовать Bootstrap для оформления форм, нужно проверить, что в проекте настроены соответствующие шаблоны и статические файлы.
Установим Bootstrap и настроим статику:
Установим Bootstrap через npm или используем CDN.
npm install bootstrap
Или подключите Bootstrap через CDN в базовом шаблоне:
templates/base.html
<!DOCTYPE html> <html lang="ru"> <head> <!-- ... --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> <!-- ... --> </head> <body> <!-- ... --> {% block content %}{% endblock %} <!-- ... --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
Настроим статические файлы
settings.py
STATIC_URL = '/static/' STATICFILES_DIRS = [ BASE_DIR / "static", ]
Для подтверждения входа с помощью одноразовых кодов пользователю может быть отправлено сообщение на email или SMS. Рассмотрим отправку на email.
Настройка email в settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.your-email-provider.com' EMAIL_PORT = 587 # или 465 для SSL EMAIL_USE_TLS = True # или EMAIL_USE_SSL = True, если используете SSL EMAIL_HOST_USER = 'your-email@example.com' EMAIL_HOST_PASSWORD = 'your-email-password' DEFAULT_FROM_EMAIL = 'Your Project Name <your-email@example.com>'
По умолчанию django-two-factor-auth
поддерживает несколько методов доставки токенов:
-
Генерация кода в приложении-аутентификаторе, таком как Google Authenticator или Authy.
-
Отправка одноразовых паролей по SMS или электронной почте.
Если хочется отправлять коды по электронной почте, убедитесь, что в settings.py
настроена соответствующая доставка.
settings.py
TWO_FACTOR_EMAIL_GATEWAY = 'two_factor.gateways.email.EmailDevice'
Если вы планируете использовать SMS, нужно настроить SMS-шлюз, например, с помощью Twilio.
Если вы используете кастомную модель пользователя, убедитесь, что она совместима с django-two-factor-auth
. Обычно достаточно того, что модель наследуется от AbstractUser
или AbstractBaseUser
.
Переопределим представления входа, чтобы включить двухфакторную аутентификацию.
urls.py
from django.urls import path, include from two_factor.urls import urlpatterns as tf_urls from django.contrib.auth import views as auth_views urlpatterns = [ # ... path('', include(tf_urls)), # Маршруты для двухфакторной аутентификации # ... ]
Теперь при переходе на /account/login/
будет использоваться представление входа с поддержкой двухфакторной аутентификации.
Можно настроить, куда перенаправлять пользователя после успешного входа или выхода.
settings.py
LOGIN_REDIRECT_URL = '/dashboard/' # После успешного входа LOGOUT_REDIRECT_URL = '/account/login/' # После выхода
Пакет django-two-factor-auth
поставляется с базовыми шаблонами, но можно переопределить их, создав свои собственные в каталоге templates/
.
Например, чтобы изменить шаблон ввода одноразового пароля, создайте файл templates/two_factor/core/otp.html
и добавьте в него код.
templates/two_factor/core/otp.html
{% extends "base.html" %} {% block content %} <h2>Ввод одноразового пароля</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit" class="btn btn-primary">Подтвердить</button> </form> {% endblock %}
Если вы используете административный интерфейс Django, можно защитить его с помощью двухфакторной аутентификации.
admin.py
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth import get_user_model from two_factor.admin import AdminSiteOTPRequired, AdminSiteOTPRequiredMixin User = get_user_model() class OTPAdminSite(AdminSiteOTPRequired): pass admin_site = OTPAdminSite(name='OTPAdmin') @admin.register(User, site=admin_site) class CustomUserAdmin(UserAdmin): pass
Дополнительные настройки
Можно настроить время жизни токенов восстановления или одноразовых паролей.
settings.py
TWO_FACTOR_REMEMBER_COOKIE_AGE = 1209600 # 14 дней в секундах
Если хочется ограничить методы доставки токенов, можно настроить это следующим образом:
TWO_FACTOR_CALL_GATEWAY = 'your_project.gateways.custom_call_gateway.CustomCallGateway' TWO_FACTOR_SMS_GATEWAY = 'your_project.gateways.custom_sms_gateway.CustomSmsGateway'
Magic Link для входа
Magic Link — это метод аутентификации без пароля, при котором пользователю отправляется специальная одноразовая ссылка на его электронную почту для входа в систему.
Для реализации Magic Link в Django воспользуемся пакетом django-magiclink
.
pip install django-magiclink
Добавим приложение magiclink
в список установленных приложений и настроим бэкенды аутентификации.
settings.py
INSTALLED_APPS = [ # ... 'magiclink', # ... ] AUTHENTICATION_BACKENDS = [ 'magiclink.backends.MagicLinkBackend', # Бэкенд Magic Link 'django.contrib.auth.backends.ModelBackend', # Стандартный бэкенд ]
Чтобы Magic Link мог отправлять письма со ссылками для входа, необходимо настроить отправку электронной почты в вашем проекте. Добавляем следующие настройки в settings.py
:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.your-email-provider.com' EMAIL_PORT = 587 # или 465 для SSL EMAIL_USE_TLS = True # или EMAIL_USE_SSL = True, если используете SSL EMAIL_HOST_USER = 'your-email@example.com' EMAIL_HOST_PASSWORD = 'your-email-password' DEFAULT_FROM_EMAIL = 'Your Project Name '
Подключим представления из пакета django-magiclink
.
urls.py
from django.urls import path, include from magiclink import urls as magiclink_urls urlpatterns = [ # ... path('accounts/', include(magiclink_urls)), # ... ]
Пакет django-magiclink
дает набор готовых URL-маршрутов и представлений для обработки запросов на отправку и активацию Magic Link.
По дефолтуdjango-magiclink
использует стандартные шаблоны для писем. Можно настроить их под свой дизайн, создав соответствующие шаблоны в вашем приложении.
Создаем файл templates/magiclink/email/email_subject.txt
с темой письма:
Ссылка для входа на сайт {{ site_name }}
И файл templates/magiclink/email/email_body.txt
с содержимым письма:
Привет, Вы запросили ссылку для входа на сайт {{ site_name }}. Перейдите по ссылке ниже, чтобы войти: {{ magic_link }} Если вы не запрашивали ссылку, просто проигнорируйте это письмо. Спасибо, Команда {{ site_name }}
В settings.py
можно настроить различные параметры работы django-magiclink
.
MAGICLINK = { 'LINK_EXPIRATION': 3600, # Время жизни ссылки в секундах (по умолчанию 3600 секунд = 1 час) 'REDIRECT_URL': '/', # URL для перенаправления после успешного входа 'EMAIL_SUBJECT_TEMPLATE': 'magiclink/email/email_subject.txt', 'EMAIL_BODY_TEMPLATE': 'magiclink/email/email_body.txt', 'SUCCESS_MESSAGE': 'Проверьте вашу электронную почту для входа в систему.', }
Создайте шаблон для формы ввода email, чтобы пользователь мог запросить Magic Link.
templates/magiclink/login.html
{% extends "base.html" %} {% block content %} <h2>Вход по Magic Link</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Отправить ссылку для входа</button> </form> {% endblock %}
Помимо этого, должен быть базовый шаблон base.html
, от которого наследуются другие шаблоны.
templates/base.html
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>{% block title %}Мой сайт{% endblock %}</title> </head> <body> {% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% block content %}{% endblock %} </body> </html>
Если хочется изменить поведение представлений, можно создать свои собственные, наследуясь от представлений django-magiclink
.
views.py
from magiclink.views import MagicLinkLoginView, MagicLinkCompleteView class CustomMagicLinkLoginView(MagicLinkLoginView): template_name = 'magiclink/login.html' class CustomMagicLinkCompleteView(MagicLinkCompleteView): success_url = '/dashboard/' # Перенаправление после успешного входа
urls.py
from django.urls import path from .views import CustomMagicLinkLoginView, CustomMagicLinkCompleteView urlpatterns = [ # ... path('accounts/login/', CustomMagicLinkLoginView.as_view(), name='magiclink_login'), path('accounts/login/complete/', CustomMagicLinkCompleteView.as_view(), name='magiclink_complete'), # ... ]
По умолчанию django-magiclink
может создавать новых пользователей при первом использовании Magic Link. Если нужно отключить автоматическое создание пользователей, добавьте следующую настройку:
settings.py
MAGICLINK = { # ... 'CREATE_USER': False, }
В этом случае, если пользователь с указанным email не существует, ссылка не будет отправлена.
Спасибо, что дочитали до конца! Если у вас остались вопросы или вы хотите поделиться своим опытом, пишите в комментариях.
А сейчас приглашаю вас на бесплатные вебинары курса Python Developer. Professional:
-
7.11. gRPC в Python. Записаться.
-
13.11. Делаем по красоте: паттерны проектирования в Python-приложениях. Записаться.
-
21.11. Основы FastAPI. Записаться.
ссылка на оригинал статьи https://habr.com/ru/articles/855086/
Добавить комментарий