Вступ
Django, як повнофункціональний веб-фреймворк, надає надійну і гнучку систему авторизації та автентифікації користувачів "з коробки". Ця система є однією з найсильніших сторін Django, оскільки вона дозволяє розробникам зосередитися на бізнес-логіці, не витрачаючи час на реалізацію базових функцій безпеки. Однак, у реальних проектах часто виникає потреба в адаптації цієї системи під унікальні вимоги, чи то додавання нових полів до моделі користувача, інтеграція із зовнішніми джерелами автентифікації, чи реалізація складних політик дозволів.
У цій статті ми зануримося в глибини системи авторизації Django. Ми почнемо з огляду вбудованих компонентів, потім розглянемо, як їх можна розширювати та кастомізувати, використовуючи сучасні підходи Python та Django. Наша мета — надати тобі всебічне розуміння, як ефективно використовувати та вдосконалювати систему авторизації Django, роблячи твої додатки безпечнішими та гнучкішими.
Ми розглянемо:
- Основи системи авторизації Django: User Model, Backend Authentication, Permissions.
- Кастомізація User Model: AbstractUser та AbstractBaseUser.
- Аутентифікаційні бекенди: Створення власних бекендів.
- Система дозволів: Ролі та дозволи, кастомні дозволи.
- Інтеграція з соціальними мережами та OAuth2.
- Двофакторна автентифікація (2FA).
- Найкращі практики безпеки.
Поїхали!
1. Основи системи авторизації Django
Система авторизації Django складається з кількох ключових компонентів, які працюють разом, щоб забезпечити безпеку вашого додатку.
1.1. User Model (django.contrib.auth.models.User)
Серцем системи є модель користувача. За замовчуванням Django надає User модель, яка містить основні поля, такі як username, password, email, first_name, last_name, is_active, is_staff, is_superuser та date_joined.
Приклад використання вбудованої моделі User:
# myapp/views.py
from django.contrib.auth.models import User
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.forms import AuthenticationForm
def register_user(request):
if request.method == 'POST':
username = request.POST['username']
email = request.POST['email']
password = request.POST['password']
if User.objects.filter(username=username).exists():
# Обробка помилки: користувач з таким ім'ям вже існує
pass
else:
user = User.objects.create_user(username=username, email=email, password=password)
return redirect('login') # Перенаправлення на сторінку входу
return render(request, 'registration/register.html')
def login_user(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('home') # Перенаправлення на домашню сторінку
else:
# Обробка помилки: невірні облікові дані
pass
else:
form = AuthenticationForm()
return render(request, 'registration/login.html', {'form': form})
def logout_user(request):
logout(request)
return redirect('login')
Важливо: create_user та set_password автоматично хешують пароль. Ніколи не зберігайте паролі в чистому вигляді.
1.2. Аутентифікаційні бекенди (AUTHENTICATION_BACKENDS)
Django може використовувати різні методи для автентифікації користувачів. Ці методи називаються аутентифікаційними бекендами. За замовчуванням використовується django.contrib.auth.backends.ModelBackend, який автентифікує користувачів за ім'ям користувача та паролем, зберігаючи дані в моделі User.
Ви можете налаштувати список бекендів у settings.py:
# myproject/settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
# 'myapp.backends.EmailBackend', # Ваш власний бекенд
]
При автентифікації Django перебирає всі бекенди по черзі, доки один з них не поверне користувача.
1.3. Система дозволів (Permissions)
Крім автентифікації (хто ти?), Django також керує авторизацією (що ти можеш робити?). Система дозволів дозволяє контролювати доступ до об'єктів або функцій.
- Дозволи на рівні моделі: Django автоматично створює три дозволи для кожної моделі:
add,change,delete. - Кастомні дозволи: Ви можете визначати власні дозволи в класі
Metaмоделі. - Групи: Користувачі можуть бути об'єднані в групи, і дозволи можуть надаватися групам.
Приклад використання дозволів:
# myapp/views.py
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse
@login_required # Перевіряє, чи користувач автентифікований
def dashboard(request):
return HttpResponse(f"Welcome to your dashboard, {request.user.username}!")
@permission_required('myapp.can_publish_posts') # Перевіряє дозвіл
def publish_post(request):
if request.user.has_perm('myapp.can_publish_posts'):
return HttpResponse("You can publish posts.")
else:
return HttpResponse("You don't have permission to publish posts.", status=403)
2. Кастомізація User Model
У більшості реальних проектів вам знадобляться додаткові поля для моделі користувача (наприклад, phone_number, avatar, birth_date). Django пропонує два основні способи кастомізації моделі User: розширення AbstractUser або AbstractBaseUser.
Важливо: Завжди кастомізуйте модель User на початку проекту, до виконання перших міграцій. Змінити модель User після цього значно складніше.
2.1. Розширення AbstractUser
Це рекомендований спосіб, коли вам потрібно додати нові поля до існуючої моделі User Django, але ви хочете зберегти всі її вбудовані поля та функціонал (наприклад, username, first_name, email, is_staff, is_superuser, permissions, groups). AbstractUser надає повну функціональність User моделі, і ви можете легко додавати власні поля.
- Створіть кастомну модель користувача:
# users/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) avatar = models.ImageField(upload_to='avatars/', blank=True, null=True) birth_date = models.DateField(blank=True, null=True) bio = models.TextField(blank=True) # Ви можете додати або перевизначити методи, якщо потрібно def __str__(self): return self.username - Налаштуйте
settings.py: Повідомте Django, що ви використовуєте свою кастомну модель користувача.# myproject/settings.py AUTH_USER_MODEL = 'users.CustomUser' # 'app_name.ModelName' - Виконайте міграції:
python manage.py makemigrations users python manage.py migrate
Тепер скрізь у вашому проекті, де Django посилається на User модель (наприклад, request.user), він буде використовувати вашу CustomUser модель.
2.2. Розширення AbstractBaseUser
Використовуйте AbstractBaseUser, якщо вам потрібен повністю кастомний підхід до моделі користувача, і ви хочете почати "з нуля", визначаючи лише необхідні поля (наприклад, ви не хочете використовувати username, а хочете автентифікувати користувача за email). Цей підхід вимагає більше коду, оскільки вам доведеться самостійно реалізувати управління паролями, дозволами та деякими іншими функціями.
- Створіть кастомну модель користувача та менеджер:
Вам потрібно буде визначити менеджер для вашої кастомної моделі, який оброблятиме створення користувачів та суперкористувачів.
# users/models.py from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.db import models from django.utils import timezone class CustomUserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('The Email field must be set') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) extra_fields.setdefault('is_active', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self.create_user(email, password, **extra_fields) class CustomUser(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) first_name = models.CharField(max_length=30, blank=True) last_name = models.CharField(max_length=30, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(default=timezone.now) objects = CustomUserManager() USERNAME_FIELD = 'email' # Використовуємо email для входу REQUIRED_FIELDS = ['first_name', 'last_name'] # Обов'язкові поля при створенні суперкористувача def __str__(self): return self.email def get_full_name(self): return f"{self.first_name} {self.last_name}".strip() def get_short_name(self): return self.first_nameBaseUserManager: Менеджер, який надає допоміжні методи для створення користувачів та суперкористувачів.PermissionsMixin: Додає поля та методи для системи дозволів Django (групи, дозволи).USERNAME_FIELD: Визначає унікальний ідентифікатор для автентифікації.REQUIRED_FIELDS: Список полів, які будуть запитуватися при створенні суперкористувача за допомогоюcreatesuperuser.
- Налаштуйте
settings.py(нагадаю):# myproject/settings.py AUTH_USER_MODEL = 'users.CustomUser' - Виконайте міграції (нагадаю):
python manage.py makemigrations users python manage.py migrate
Цей підхід дає вам повний контроль, але вимагає більше ручної роботи для реалізації функціоналу, який AbstractUser надає "з коробки".
3. Аутентифікаційні бекенди: Створення власних бекендів
Власні аутентифікаційні бекенди дозволяють інтегрувати Django з будь-яким зовнішнім джерелом користувачів або використовувати нестандартні методи автентифікації (наприклад, вхід через SMS-код, LDAP, OAuth).
Бекенд автентифікації повинен бути класом, який реалізує два основні методи: authenticate() та get_user().
- Створіть власний бекенд:
Наприклад, бекенд, який дозволяє входити за допомогою електронної пошти замість імені користувача (якщо ви використовуєте
AbstractUserі не змінюєтеUSERNAME_FIELD).# users/backends.py from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model class EmailBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): UserModel = get_user_model() try: # Намагаємося знайти користувача за email user = UserModel.objects.get(email=username) except UserModel.DoesNotExist: return None if user.check_password(password): return user return None def get_user(self, user_id): UserModel = get_user_model() try: return UserModel.objects.get(pk=user_id) except UserModel.DoesNotExist: return Noneauthenticate(request, username, password, **kwargs): Приймає облікові дані та повертає об'єкт користувача, якщо автентифікація успішна, інакшеNone.get_user(user_id): Приймає ID користувача та повертає об'єкт користувача. Використовується для отримання користувача з сесії.
- Зареєструйте бекенд у
settings.py:# myproject/settings.py AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', # Залишаємо стандартний, якщо хочемо username + password 'users.backends.EmailBackend', # Наш новий бекенд ]
Порядок бекендів має значення: Django буде намагатися автентифікувати користувача за допомогою кожного бекенду по черзі.
4. Система дозволів: Ролі, дозволи та кастомні дозволи
Django надає гнучку систему дозволів, яка дозволяє контролювати, хто може виконувати певні дії.
4.1. Вбудовані дозволи та групи
- Дозволи на рівні моделі: Автоматично створюються для кожної моделі (
add,change,delete). Ви можете бачити їх в адмін-панелі. - Групи (
django.contrib.auth.models.Group): Дозволяють агрегувати дозволи та призначати їх групам користувачів, а не кожному користувачу окремо. Користувач може належати до кількох груп.
4.2. Кастомні дозволи на рівні моделі
Ви можете визначити власні дозволи в класі Meta вашої моделі.
# blog/models.py
from django.db import models
from django.contrib.auth import get_user_model
class Post(models.Model):
# ... інші поля ...
title = models.CharField(max_length=250)
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='blog_posts')
# ...
class Meta:
# ... інші мета-опції ...
permissions = [
("can_publish_post", "Can publish blog post"),
("can_edit_own_post", "Can edit their own blog post"),
]
Після додавання цих дозволів вам потрібно виконати python manage.py makemigrations та python manage.py migrate. Вони з'являться в адмін-панелі, і ви зможете призначати їх користувачам або групам.
4.3. Перевірка дозволів у коді
Ви можете перевіряти дозволи у представленнях, шаблонах та моделях.
- У представленнях (views):
- Декоратори:
permission_requiredдля перевірки конкретного дозволу,user_passes_testдля власної логіки.from django.contrib.auth.decorators import permission_required @permission_required('blog.can_publish_post', raise_exception=True) def publish_view(request): return HttpResponse("Ви маєте дозвіл на публікацію постів.") - Методи об'єкта
request.user:has_perm(),has_perms(),has_module_perms().def edit_post(request, post_id): post = get_object_or_404(Post, id=post_id) if request.user.has_perm('blog.can_edit_own_post') and request.user == post.author: # Дозволено редагувати pass else: # Відмовлено у доступі pass
- Декоратори:
- У шаблонах (templates):
{% if perms.blog.can_publish_post %} <a href="/publish/">Опублікувати новий пост</a> {% endif %} {% if user.is_authenticated %} Привіт, {{ user.username }}! {% else %} <a href="{% url 'login' %}">Увійти</a> {% endif %}permsглобальна змінна, яка надає доступ до дозволів поточного користувача.
4.4. Дозволи на рівні об'єктів (Object-level permissions)
Вбудована система дозволів Django є "глобальною" (користувач може або не може виконувати дію на всіх об'єктах певного типу). Однак, часто потрібно контролювати доступ до конкретного об'єкта (наприклад, користувач може редагувати ТІЛЬКИ свої власні пости).
Для цього Django пропонує абстрактну основу, а реалізація зазвичай досягається за допомогою сторонніх бібліотек, таких як django-guardian або django-rules.
Приклад з django-guardian:
- Встановлення:
pip install django-guardian - Налаштування
settings.py:# myproject/settings.py INSTALLED_APPS = [ # ... 'guardian', # ... ] AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend', # Додаємо Guardian Backend ] - Виконайте міграції:
python manage.py migrate - Використання в коді:
# post/views.py from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from guardian.decorators import permission_required from .models import Post @permission_required('blog.change_post', (Post, 'id', 'post_id')) def edit_post_object(request, post_id): post = get_object_or_404(Post, id=post_id) # Тепер ми гарантовано маємо дозвіл на зміну цього конкретного поста return HttpResponse(f"Ви можете редагувати пост: {post.title}")permission_required('blog.change_post', (Post, 'id', 'post_id'))перевіряє, чи має користувач дозвілchange_postна об'єктPostзid, що відповідає значеннюpost_idз URL.
5. Інтеграція із зовнішніми джерелами автентифікації та OAuth2
У сучасному веб-додатку часто потрібна автентифікація через соціальні мережі (Google, Facebook, GitHub) або за допомогою протоколу OAuth2.
Для Django існує чудова бібліотека django-allauth, яка надає повний набір функцій для автентифікації, реєстрації та управління акаунтами, включаючи інтеграцію з різними провайдерами OAuth.
- Встановлення:
pip install django-allauth - Налаштування
settings.py:# myproject/settings.py INSTALLED_APPS = [ # ... Django apps ... 'django.contrib.sites', # Потрібно для django-allauth 'allauth', 'allauth.account', 'allauth.socialaccount', # ... додайте провайдерів, наприклад: 'allauth.socialaccount.providers.google', 'allauth.socialaccount.providers.github', # ... ] AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', # Необхідно для Django Admin 'allauth.account.auth_backends.AuthenticationBackend', # Allauth Backend ] SITE_ID = 1 # Унікальний ID сайту, якщо у вас їх кілька # Додаткові налаштування allauth ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_USERNAME_REQUIRED = False # Якщо ви автентифікуєте за email ACCOUNT_AUTHENTICATION_METHOD = 'email' ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # Обов'язкова верифікація email ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 5 # Захист від брутфорсу ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 300 # 5 хвилин таймаут LOGIN_REDIRECT_URL = '/' # Куди перенаправляти після входу LOGOUT_REDIRECT_URL = '/accounts/login/' # Куди перенаправляти після виходу SOCIALACCOUNT_PROVIDERS = { 'google': { 'APP': { 'client_id': 'YOUR_GOOGLE_CLIENT_ID', 'secret': 'YOUR_GOOGLE_CLIENT_SECRET', 'key': '' } }, # ... інші провайдери ... } - Додайте URL-и
allauthдо головногоurls.py:# myproject/myproject/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('allauth.urls')), # URL-и Allauth # ... інші URL-и вашого проекту ] - Виконайте міграції:
python manage.py migrate - Налаштуйте провайдерів в адмін-панелі: Зайдіть в адмін-панель (
/admin/), перейдіть до "Sites" та "Social applications", щоб додати ваші клієнтські ID та секрети для кожного провайдера.
django-allauth значно спрощує інтеграцію з різними провайдерами та забезпечує багато функціоналу "з коробки" (реєстрація, відновлення пароля, верифікація email).
6. Двофакторна автентифікація (2FA)
Двофакторна автентифікація значно підвищує безпеку, вимагаючи другого фактора перевірки (наприклад, код з мобільного додатку, SMS) після введення пароля.
Для Django існують бібліотеки, які допомагають реалізувати 2FA, наприклад django-two-factor-auth.
- Встановлення:
pip install django-two-factor-auth - Налаштування
settings.py:# myproject/settings.py INSTALLED_APPS = [ # ... 'django_otp', 'django_otp.plugins.otp_totp', # Для TOTP (Google Authenticator) 'django_otp.plugins.otp_static', # Для статичних паролів 'two_factor', # ... ] AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', 'two_factor.backends.TwoFactorAuthBackend', # Two-factor Backend ] # Якщо ви використовуєте email для входу (з CustomUser або allauth) OTP_TOTP_ISSUER = "My Django App" # Назва вашого додатку для Google Authenticator LOGIN_URL = 'two_factor:login' # Перенаправлення на 2FA логін - Додайте URL-и
two_factor:# myproject/myproject/urls.py from django.contrib import admin 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('admin/', admin.site.urls), path('', include(tf_urls)), # URL-и двофакторної автентифікації path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), # URL для виходу # ... інші URL-и вашого проекту ] - Виконайте міграції:
python manage.py migrate
Після цього користувачі зможуть налаштовувати 2FA у своїх профілях, використовуючи, наприклад, Google Authenticator.
7. Найкращі практики безпеки авторизації в Django
Надійний механізм авторизації — це не тільки функціональність, але й безпека. Дотримуйтесь цих практик:
- Використовуйте
AbstractUserабоAbstractBaseUserз самого початку: Плануйте свою модель користувача заздалегідь. - Завжди хешуйте паролі: Django робить це автоматично за допомогою
set_password(),create_user(),create_superuser(). Ніколи не вимикайте це і не створюйте власний механізм хешування без глибокого розуміння криптографії. - HTTPS для всього трафіку: У продакшені завжди використовуйте HTTPS для захисту передачі облікових даних.
- Увімкніть захист від CSRF: Django надає вбудований захист від Cross-Site Request Forgery. Переконайтеся, що ви використовуєте
{% csrf_token %}у всіх формах POST. - Захист від XSS: Система шаблонів Django автоматично екранує HTML, захищаючи від XSS-атак. Будьте обережні при використанні
|safeфільтра. - Обмеження спроб входу (Brute-force protection): Використовуйте бібліотеки, такі як
django-axesабо вбудовані можливостіdjango-allauth, щоб блокувати IP-адреси або користувачів після кількох невдалих спроб входу. - Надійна політика паролів: Заохочуйте користувачів використовувати складні паролі. Ви можете використовувати
PASSWORD_HASHERSтаAUTH_PASSWORD_VALIDATORSуsettings.pyдля налаштування вимог до паролів. - Верифікація Email: Завжди вимагайте верифікацію електронної пошти для нових користувачів, щоб запобігти створенню фейкових акаунтів.
- Управління сесіями: Пам'ятайте про налаштування сесій (
SESSION_COOKIE_SECURE,SESSION_COOKIE_HTTPONLYтощо) для підвищення безпеки. - Регулярні оновлення Django та залежностей: Завжди оновлюйте Django та всі сторонні бібліотеки до останніх версій, щоб отримувати виправлення безпеки.
- Логування та моніторинг: Відстежуйте підозрілі активності, такі як численні невдалі спроби входу або зміни в дозволах.
Висновок
Система авторизації Django — це потужний, але водночас гнучкий інструмент, який лежить в основі безпеки будь-якого веб-додатку. Розуміючи її внутрішню роботу та знаючи, як її ефективно розширювати, ви можете створювати надійні та безпечні системи, які відповідають найсучаснішим вимогам.
Ми розглянули, як використовувати вбудовану модель користувача, кастомізувати її за допомогою AbstractUser та AbstractBaseUser, створювати власні автентифікаційні бекенди, керувати дозволами на рівні моделі та об'єктів, а також інтегрувати зовнішні сервіси через django-allauth та додавати двофакторну автентифікацію.
Пам'ятайте, що безпека — це безперервний процес. Завжди дотримуйтесь найкращих практик, будьте в курсі нових загроз та оновлень, і ваші додатки будуть максимально захищеними. Завдяки гнучкості та потужності Django, ви маєте всі необхідні інструменти для побудови досконалої системи авторизації.
Продовжуйте вивчати, експериментувати та створювати!