Аутентификация и авторизация в Django: полное руководство пользователя

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Профессиональные Django-разработчики
  • Студенты и начинающие программисты, желающие углубить знания в Django
  • Разработчики, ищущие рекомендации по настройке аутентификации и авторизации в своих проектах

    Первый пользовательский вопрос, с которым сталкивается каждый Django-разработчик: "Как управлять доступом к моему приложению?". Именно от грамотно настроенной аутентификации и авторизации зависит, насколько безопасным и удобным будет ваше веб-приложение. Но разница между этими понятиями, настройка кастомных моделей пользователей и реализация сложной логики разграничения прав часто становятся камнем преткновения даже для опытных программистов. Сегодня разбираемся с нуля, как правильно настроить всё это в Django — с рабочими примерами кода, который вы сможете адаптировать под свои задачи. 🔐

Если вы хотите освоить Django на профессиональном уровне и создавать безопасные веб-приложения, обратите внимание на Обучение Python-разработке от Skypro. Курс построен на практических задачах, включая детальное изучение систем аутентификации и авторизации, с которыми вы будете работать в реальных проектах. Преподаватели — действующие разработчики, которые покажут, как избежать типичных ошибок при работе с Django-аутентификацией и разграничением прав.

Что такое аутентификация и авторизация в Django

Прежде чем погрузиться в код, давайте чётко разграничим два ключевых понятия, которые часто путают между собой:

  • Аутентификация — процесс проверки личности пользователя (кто вы?)
  • Авторизация — процесс определения, что пользователю разрешено делать (что вам можно?)

Django предоставляет мощную встроенную систему для обоих процессов, которая реализуется через несколько компонентов:

Компонент Назначение Ключевые функции
django.contrib.auth Ядро системы аутентификации Базовые модели User, Group; хеширование паролей
Authentication backends Механизмы проверки учетных данных ModelBackend (стандартный), другие бэкенды (LDAP, OAuth)
Permission system Система разрешений Проверка прав на уровне модели и объекта
Django Admin Административный интерфейс Управление пользователями и группами

Максим Ковалёв, Senior Django-разработчик

Однажды мы разрабатывали платформу для врачей, где доступ к медицинским данным требовал особой защиты. Я решил использовать стандартную систему Django для быстрого запуска. Большая ошибка! Уже через месяц нам потребовалось добавить несколько уровней доступа для разных специалистов, а стандартная модель User не поддерживала нужную гибкость.

Пришлось переписывать всю логику, когда система уже работала с реальными данными. Теперь я всегда начинаю с планирования кастомной модели пользователя, даже если проект кажется простым. Поверьте, нет ничего сложнее, чем мигрировать пользовательские данные на новую модель в действующем приложении.

Для типичного веб-приложения процесс работы с пользователем выглядит так:

  1. Пользователь регистрируется (создается запись в таблице User)
  2. Система сохраняет пароль в хешированном виде (PBKDF2 + SHA256 по умолчанию)
  3. При последующих входах введенный пароль хешируется и сравнивается с сохраненным
  4. После успешного входа создается сессия, хранящая информацию о пользователе
  5. При доступе к защищенным ресурсам система проверяет права пользователя

Django использует модель User из пакета django.contrib.auth.models для хранения информации о пользователях. Эта модель включает базовые поля: username, email, password, firstname, lastname, isactive, isstaff и is_superuser. 🧩

Пошаговый план для смены профессии

Базовая настройка системы аутентификации Django

Включить систему аутентификации в Django просто. Если вы использовали команду startproject, то базовая конфигурация уже присутствует в settings.py.

Проверьте, что в INSTALLED_APPS есть необходимые приложения:

INSTALLED_APPS = [
# ...
'django.contrib.auth', # Система аутентификации
'django.contrib.contenttypes', # Необходима для работы auth
# ...
]

Убедитесь, что в MIDDLEWARE присутствуют необходимые компоненты:

MIDDLEWARE = [
# ...
'django.contrib.sessions.middleware.SessionMiddleware', # Для работы с сессиями
# ...
'django.contrib.auth.middleware.AuthenticationMiddleware', # Для аутентификации
# ...
]

Также важно настроить параметры аутентификации в settings.py:

# URL для перенаправления после успешного входа
LOGIN_REDIRECT_URL = 'home'

# URL для перенаправления, если доступ запрещен
LOGIN_URL = 'login'

# URL для перенаправления после выхода
LOGOUT_REDIRECT_URL = 'home'

# Бэкенды аутентификации
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
# Здесь можно добавить кастомные бэкенды
]

# Хешеры паролей (в порядке их применения)
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

После настройки выполните миграции, чтобы создать необходимые таблицы в базе данных:

python manage.py migrate

Теперь вы можете создать суперпользователя для доступа к административной панели:

python manage.py createsuperuser

С базовой настройкой у вас уже есть возможность:

  • Создавать и управлять пользователями через Django Admin
  • Аутентифицировать пользователей с помощью form-based аутентификации
  • Контролировать доступ к представлениям с помощью декораторов
  • Проверять и управлять разрешениями пользователей

Однако для большинства реальных приложений этого недостаточно. Перейдем к созданию кастомной модели пользователя. 🔧

Создание кастомной модели пользователя

Стандартная модель User в Django хороша для быстрого старта, но для серьезных проектов вам почти наверняка потребуется кастомная модель. И важно спланировать это до начала разработки, поскольку изменить модель User после создания миграций крайне сложно.

Django предлагает несколько способов расширения стандартной модели User:

Подход Преимущества Недостатки Когда использовать
Proxy Model Простота реализации, не требует миграций Нельзя добавить новые поля Для изменения поведения без изменения структуры
OneToOneField (Profile) Сохраняет совместимость с auth.User Требует дополнительных запросов к БД Для добавления полей к существующему проекту
AbstractUser Сохраняет функционал User с возможностью расширения Жесткая привязка к username Когда вас устраивает базовая структура auth.User
AbstractBaseUser Полная свобода в определении структуры Требует реализации многих методов вручную Для полностью кастомной системы аутентификации

Наиболее гибкий подход — наследование от AbstractBaseUser. Рассмотрим его реализацию:

Сначала создайте модель пользователя в вашем приложении (например, accounts/models.py):

Python
Скопировать код
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.utils import timezone

class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email is required')
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)

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)

# Дополнительные поля для вашего приложения
phone_number = models.CharField(max_length=15, blank=True)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)

objects = CustomUserManager()

USERNAME_FIELD = 'email' # Поле для аутентификации (вместо username)
REQUIRED_FIELDS = [] # Дополнительные обязательные поля

def __str__(self):
return self.email

def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip() or self.email

def get_short_name(self):
return self.first_name or self.email

Затем укажите Django использовать вашу кастомную модель в settings.py:

AUTH_USER_MODEL = 'accounts.CustomUser'

Не забудьте создать и применить миграции:

python manage.py makemigrations
python manage.py migrate

Для работы с кастомной моделью в административной панели, создайте файл accounts/admin.py:

Python
Скопировать код
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ('email', 'first_name', 'last_name', 'is_staff', 'is_active')
list_filter = ('is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'phone_number', 'bio', 'avatar')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email', 'first_name', 'last_name')
ordering = ('email',)

admin.site.register(CustomUser, CustomUserAdmin)

Преимущество этого подхода заключается в том, что вы полностью контролируете модель пользователя и можете адаптировать её под требования вашего проекта. 👨‍💻

Анна Соколова, Python-разработчик

Я работала над платформой для онлайн-курсов, где требовалось разделять пользователей на студентов, преподавателей и администраторов. Поначалу использовали стандартную модель User и группы для разграничения ролей. Но когда потребовалось добавить специфичные для каждой роли поля (для студентов — прогресс обучения, для преподавателей — опыт и специализация), код стал запутанным.

Мы переписали систему с использованием AbstractBaseUser и добавили поле user_type с выбором типа пользователя. Затем создали отдельные модели Student, Teacher и Administrator, связанные с основной моделью через OneToOneField. Это позволило нам иметь единую систему аутентификации, но при этом гибко работать с различными типами пользователей.

Главный урок, который я извлекла: планируйте модель пользователя на этапе проектирования архитектуры. Лучше потратить день на создание кастомной модели, чем неделю на миграцию данных в производственной среде.

Регистрация и вход в приложении на Django

После настройки кастомной модели пользователя нам нужно реализовать регистрацию, вход и выход из системы. Django предоставляет несколько готовых классов представлений для этих задач, но часто требуется их кастомизация. 🔑

Начнем с создания форм в accounts/forms.py:

Python
Скопировать код
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
from django.contrib.auth import get_user_model

User = get_user_model()

class CustomUserCreationForm(UserCreationForm):
"""Форма для регистрации новых пользователей"""
email = forms.EmailField(
max_length=254,
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Email'})
)

password1 = forms.CharField(
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Пароль'})
)

password2 = forms.CharField(
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Подтверждение пароля'})
)

class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'password1', 'password2')
widgets = {
'first_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Имя'}),
'last_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Фамилия'}),
}

def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Пользователь с таким email уже существует")
return email


class CustomAuthenticationForm(AuthenticationForm):
"""Форма для аутентификации пользователей"""
username = forms.EmailField(
max_length=254,
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Email'})
)

password = forms.CharField(
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Пароль'})
)

Теперь создадим представления в accounts/views.py:

Python
Скопировать код
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.views import View
from django.utils.decorators import method_decorator
from django.urls import reverse_lazy
from django.contrib import messages
from django.views.generic import CreateView, TemplateView

from .forms import CustomUserCreationForm, CustomAuthenticationForm

class SignUpView(CreateView):
form_class = CustomUserCreationForm
template_name = 'accounts/signup.html'
success_url = reverse_lazy('login')

def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Регистрация успешна! Теперь вы можете войти.')
return response


class LoginView(View):
form_class = CustomAuthenticationForm
template_name = 'accounts/login.html'

def get(self, request):
form = self.form_class()
return render(request, self.template_name, {'form': form})

def post(self, request):
form = self.form_class(data=request.POST)
if form.is_valid():
email = form.cleaned_data.get('username') # В форме поле называется username
password = form.cleaned_data.get('password')
user = authenticate(username=email, password=password)
if user is not None:
login(request, user)
messages.success(request, f'Добро пожаловать, {user.get_short_name()}!')
return redirect('home')
return render(request, self.template_name, {'form': form})


def logout_view(request):
logout(request)
messages.success(request, 'Вы успешно вышли из системы.')
return redirect('home')


@method_decorator(login_required, name='dispatch')
class ProfileView(TemplateView):
template_name = 'accounts/profile.html'

Затем добавим URL-маршруты в accounts/urls.py:

Python
Скопировать код
from django.urls import path
from . import views

urlpatterns = [
path('signup/', views.SignUpView.as_view(), name='signup'),
path('login/', views.LoginView.as_view(), name='login'),
path('logout/', views.logout_view, name='logout'),
path('profile/', views.ProfileView.as_view(), name='profile'),
]

Не забудьте включить их в главный urls.py проекта:

Python
Скопировать код
from django.urls import path, include

urlpatterns = [
# ...
path('accounts/', include('accounts.urls')),
# ...
]

Наконец, создадим шаблоны для наших представлений. Вот пример шаблона для регистрации (templates/accounts/signup.html):

HTML
Скопировать код
{% extends 'base.html' %}

{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">Регистрация</div>
<div class="card-body">
<form method="post">
{% csrf_token %}

{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}

<div class="form-group mb-3">
{{ form.email.label_tag }}
{{ form.email }}
{% if form.email.errors %}
<div class="text-danger">{{ form.email.errors }}</div>
{% endif %}
</div>

<div class="form-group mb-3">
{{ form.first_name.label_tag }}
{{ form.first_name }}
</div>

<div class="form-group mb-3">
{{ form.last_name.label_tag }}
{{ form.last_name }}
</div>

<div class="form-group mb-3">
{{ form.password1.label_tag }}
{{ form.password1 }}
{% if form.password1.errors %}
<div class="text-danger">{{ form.password1.errors }}</div>
{% endif %}
<small class="form-text text-muted">{{ form.password1.help_text }}</small>
</div>

<div class="form-group mb-3">
{{ form.password2.label_tag }}
{{ form.password2 }}
{% if form.password2.errors %}
<div class="text-danger">{{ form.password2.errors }}</div>
{% endif %}
</div>

<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
</div>
<div class="card-footer">
Уже есть аккаунт? <a href="{% url 'login' %}">Войти</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

Для аутентификации через социальные сети можно использовать библиотеку python-social-auth или django-allauth. Они предоставляют готовые решения для интеграции OAuth с популярными сервисами. 🌐

Разграничение прав доступа с декораторами и миксинами

После настройки аутентификации необходимо реализовать авторизацию — то есть определить, что каждый пользователь может делать в вашем приложении. Django предлагает несколько уровней контроля доступа:

  1. Проверка аутентификации (пользователь вошел в систему)
  2. Проверка разрешений на уровне модели (пользователь имеет определенное право)
  3. Проверка разрешений на уровне объекта (пользователь может работать с конкретным объектом)
  4. Кастомные проверки (любая произвольная логика)

Рассмотрим основные инструменты для реализации авторизации.

1. Декораторы для функциональных представлений

Самый простой способ ограничить доступ — использовать декоратор login_required:

Python
Скопировать код
from django.contrib.auth.decorators import login_required

@login_required
def profile_view(request):
# Доступно только аутентифицированным пользователям
return render(request, 'profile.html')

Для более сложной логики используйте декоратор userpassestest:

Python
Скопировать код
from django.contrib.auth.decorators import user_passes_test

def is_staff_user(user):
return user.is_authenticated and user.is_staff

@user_passes_test(is_staff_user)
def staff_dashboard(request):
# Доступно только сотрудникам
return render(request, 'staff_dashboard.html')

Для проверки разрешений используйте декоратор permission_required:

Python
Скопировать код
from django.contrib.auth.decorators import permission_required

@permission_required('app.add_model')
def create_view(request):
# Доступно только пользователям с правом 'app.add_model'
return render(request, 'create.html')

# Проверка нескольких разрешений
@permission_required(['app.add_model', 'app.change_model'])
def advanced_view(request):
# Пользователь должен иметь оба разрешения
return render(request, 'advanced.html')

2. Миксины для классовых представлений

Для классовых представлений Django предоставляет аналогичные миксины:

Python
Скопировать код
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
from .models import Article

class ArticleListView(ListView):
model = Article
# Доступно всем пользователям

class ArticleDetailView(LoginRequiredMixin, DetailView):
model = Article
# Доступно только аутентифицированным пользователям
login_url = '/accounts/login/' # URL для перенаправления неаутентифицированных пользователей

class StaffArticleCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = Article
fields = ['title', 'content']

def test_func(self):
# Проверка, является ли пользователь сотрудником
return self.request.user.is_staff

def handle_no_permission(self):
# Кастомная обработка при отсутствии прав
return redirect('access_denied')

class ArticleUpdateView(PermissionRequiredMixin, UpdateView):
model = Article
fields = ['title', 'content']
permission_required = 'app.change_article'

3. Разрешения на уровне объекта

Django также поддерживает разрешения на уровне объекта через методы has_permission в моделях:

Python
Скопировать код
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
permissions = [
("publish_article", "Can publish article"),
("feature_article", "Can feature article on homepage"),
]

def user_can_edit(self, user):
# Автор или сотрудник может редактировать
return user == self.author or user.is_staff

def user_can_delete(self, user):
# Только автор или админ может удалить
return user == self.author or user.is_superuser

def user_can_view(self, user):
# Неопубликованные статьи видны только автору и сотрудникам
return self.published or user == self.author or user.is_staff

В представлении это можно использовать так:

Python
Скопировать код
class ArticleDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = Article

def test_func(self):
article = self.get_object()
return article.user_can_view(self.request.user)

4. Кастомные бэкенды разрешений

Для сложной логики авторизации можно создать кастомный бэкенд разрешений:

Python
Скопировать код
# myapp/permissions.py
class CustomPermissionBackend:
def has_perm(self, user_obj, perm, obj=None):
# Пример: проверка принадлежности к определенному отделу
if not user_obj.is_authenticated:
return False

if perm == 'app.special_action':
return hasattr(user_obj, 'department') and user_obj.department == 'special'

return False

def has_module_perms(self, user_obj, app_label):
return user_obj.is_authenticated

Добавьте этот бэкенд в AUTHENTICATION_BACKENDS в settings.py:

AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend', # Стандартный бэкенд
'myapp.permissions.CustomPermissionBackend', # Наш кастомный бэкенд
]

Таким образом, Django предоставляет гибкую и многоуровневую систему разграничения доступа, которую можно адаптировать под любые требования вашего приложения. 🛡️

Аутентификация и авторизация — фундаментальные компоненты безопасности любого Django-приложения. Как мы рассмотрели, фреймворк предоставляет мощные инструменты для их реализации: от кастомных моделей пользователей до гибких механизмов разграничения прав. Ключ к успешной архитектуре безопасности — планирование на раннем этапе и глубокое понимание доступных компонентов Django. Начните с правильной модели пользователя, внедрите надежные формы входа и регистрации, а затем постройте многоуровневую систему разграничения прав, соответствующую потребностям вашего проекта. И помните: в вопросах безопасности лучше потратить больше времени на этапе проектирования, чем исправлять уязвимости в рабочем приложении.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Какой модуль необходимо добавить в файл settings.py для использования системы аутентификации Django?
1 / 5

Загрузка...