Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Аутентификация: Пользователи
auth_users

Аутентификация: Пользователи

User модель, login/logout, password hashing, custom user model

Аутентификация и модель пользователя

Аутентификация отвечает на вопрос «кто сделал запрос». Авторизация — «может ли этот пользователь выполнить действие». Django поставляет модель пользователя, password hashing, сессии, authentication backends и готовые views; прикладной код должен использовать эти контракты, а не собирать login из отдельных полей POST.

Материал актуален для Django 5.2 LTS и 6.0.

#Что хранится в сессии после login

authenticate() проверяет credentials через AUTHENTICATION_BACKENDS и возвращает пользователя либо None. login() сохраняет в сессии id пользователя, путь backend и session auth hash, а также защищается от session fixation сменой/очисткой ключа сессии в нужных случаях.

В прикладной view лучше использовать AuthenticationForm: она вызывает authenticate(), учитывает is_active стандартного backend и возвращает одинаковую ошибку для неверного логина или пароля.

from django.contrib.auth import login from django.contrib.auth.forms import AuthenticationForm from django.shortcuts import redirect, render from django.views.decorators.http import require_http_methods @require_http_methods(["GET", "POST"]) def sign_in(request): data = request.POST if request.method == "POST" else None form = AuthenticationForm(request=request, data=data) if request.method == "POST" and form.is_valid(): login(request, form.get_user()) return redirect("dashboard") return render(request, "registration/login.html", {"form": form})

Этот короткий пример не включает защиту от массового перебора. Django core не ограничивает частоту попыток login: rate limit добавляют на reverse proxy или проверенным приложением с учётом NAT, распределённых workers, доверенных proxy и политики разблокировки. Для чувствительных систем также нужны MFA/passkeys и журнал событий.

#Готовые auth views обычно безопаснее

Подключение включает login, logout, password change и password reset:

from django.urls import include, path urlpatterns = [ path("accounts/", include("django.contrib.auth.urls")), ]

Шаблоны по умолчанию находятся под registration/, например registration/login.html. Пути можно подключить под другим prefix, заменить отдельную view или передать ей template_name:

from django.contrib.auth.views import LoginView path( "sign-in/", LoginView.as_view(template_name="accounts/sign_in.html"), name="login", )

LOGIN_URL, LOGIN_REDIRECT_URL и LOGOUT_REDIRECT_URL принимают именованные маршруты или URL. Built-in LoginView проверяет параметр next и не перенаправляет на произвольный внешний host.

Не заменяйте это сырой строкой:

# Небезопасно: open redirect на ?next=https://attacker.example/ return redirect(request.GET.get("next", "dashboard"))

Если custom workflow действительно должен обработать redirect target, применяйте url_has_allowed_host_and_scheme() с явным набором разрешённых hosts и корректным HTTPS-флагом.

Password reset состоит из четырёх views и одноразового ограниченного по времени token. Настройте почту, домен/HTTPS, нейтральный ответ, rate limit и тест полного перехода по ссылке. Не раскрывайте, существует ли email, отдельным сообщением.

#Logout — изменение состояния

В актуальном Django LogoutView выполняет logout по POST. Шаблон использует форму:

<form method="post" action="{% url 'logout' %}"> {% csrf_token %} <button type="submit">Выйти</button> </form>

logout(request) полностью очищает данные текущей сессии. Если после выхода нужно сохранить неперсональное сообщение, добавляйте его после вызова logout().

from django.contrib.auth import logout from django.shortcuts import redirect from django.views.decorators.http import require_POST @require_POST def sign_out(request): logout(request) return redirect("home")

Logout по GET уязвим для невольного запуска через ссылку, изображение или crawler. Старую ссылку <a href="...logout..."> заменяют POST-формой с CSRF.

#Пароль нельзя присваивать полю напрямую

from django.contrib.auth import get_user_model User = get_user_model() credentials = {User.USERNAME_FIELD: identifier} user = User.objects.create_user(password=raw_password, **credentials) user.set_password(new_raw_password) user.save(update_fields=["password"])

create_user() и set_password() создают hash. User.objects.create(password=raw_password) сохранит непригодную и опасную строку как будто это encoded password.

set_password() не запускает AUTH_PASSWORD_VALIDATORS; он только хэширует. Пользовательский ввод проверяют готовыми UserCreationForm, PasswordChangeForm, SetPasswordForm или явным validate_password() с пользователем.

check_password() сравнивает введённый пароль с hash. Для login используйте authenticate(), потому что backend может иметь дополнительные правила и другой USERNAME_FIELD.

#Смена пароля и сессии

Session auth hash зависит от password hash. После изменения пароля старые сессии перестают проходить проверку. Чтобы оставить текущий браузер авторизованным после контролируемой смены, Django вызывает:

from django.contrib.auth import update_session_auth_hash user.set_password(validated_password) user.save(update_fields=["password"]) update_session_auth_hash(request, user)

Готовый PasswordChangeView/PasswordChangeForm уже решает validation и сохранение текущей сессии. Остальные сессии пользователя останутся с прежним hash и будут инвалидированы.

Если secret key ротируется через SECRET_KEY_FALLBACKS, session auth hash может временно проверяться с fallback; удаляйте старые keys по плану ротации.

#Выбор password hasher

Для новых password Django использует первый класс из PASSWORD_HASHERS. Остальные нужны, чтобы проверять старые hashes и постепенно обновлять их при успешном login. Django не выбирает «самый безопасный из установленных» автоматически.

По умолчанию первым стоит PBKDF2. Для Argon2id установите дополнительную зависимость и поставьте hasher первым:

python -m pip install "django[argon2]"
PASSWORD_HASHERS = [ "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", "django.contrib.auth.hashers.ScryptPasswordHasher", ]

Не удаляйте legacy hasher, пока в базе остаются созданные им пароли: иначе пользователи не смогут войти и автоматически перейти на новый алгоритм. Параметры Argon2/PBKDF2 выбирают по бюджету CPU и памяти, измеряя login на production-классе инфраструктуры.

AUTH_PASSWORD_VALIDATORS проверяет качество выбранного пользователем пароля, а hasher защищает сохранённый пароль; это разные слои. Значение настройки по умолчанию пустое, хотя шаблон startproject обычно добавляет стандартный набор validators.

#Стандартный User: важные факты

Встроенный django.contrib.auth.models.User использует уникальный username, но email по умолчанию не уникален. is_staff разрешает вход в admin при активном пользователе, а is_superuser заставляет permission backend считать все permissions выданными.

Поля is_staff и is_superuser независимы. create_superuser() корректно выставляет оба, но ручное присваивание is_superuser=True не меняет is_staff автоматически.

is_active=False блокирует вход через стандартные ModelBackend и формы, но смысл зависит от custom backend. Для деактивации обычно сохраняют запись ради ссылочной целостности и аудита, а не удаляют пользователя.

request.user — экземпляр user model или AnonymousUser. Проверяйте request.user.is_authenticated, не сравнивайте типы и не вызывайте это свойство как функцию.

#Custom user нужно выбрать до первых миграций

Практичный старт — тонкий наследник AbstractUser, даже если пока добавлено одно поле:

# accounts/models.py from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): locale = models.CharField(max_length=10, default="ru") time_zone = models.CharField(max_length=64, default="Europe/Moscow")
AUTH_USER_MODEL = "accounts.User"

Модель должна появиться в первой миграции приложения. Смена AUTH_USER_MODEL после появления зависимых таблиц — отдельная сложная миграция данных и связей, а не обычная правка settings.

Не задавайте custom model таблицу auth_user: это имя стандартной таблицы и создаёт конфликт или путаницу миграций.

AbstractBaseUser нужен, когда меняется сам идентификатор и контракт пользователя. Тогда разработчик обязан корректно реализовать manager, USERNAME_FIELD, обязательные поля, admin/forms и поведение flags. Для типового проекта AbstractUser дешевле и надёжнее.

Email-login требует явной политики нормализации и уникальности на уровне базы. Проверка .exists() перед созданием не защищает от гонки. Также решите, чувствителен ли local part к регистру и как обрабатываются существующие дубликаты.

#Ссылки на user model

В моделях используйте строку settings:

from django.conf import settings from django.db import models class Post(models.Model): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="posts", )

В runtime-коде используйте get_user_model():

from django.contrib.auth import get_user_model User = get_user_model()

Миграции, зависящие от user model, используют migrations.swappable_dependency(settings.AUTH_USER_MODEL). Не пишите raw SQL к auth_user: имя таблицы, primary key и поля custom model могут отличаться.

#Регистрация

UserCreationForm подтверждает пароль, запускает настроенные password validators и вызывает set_password(). Для custom user создайте наследника с явным Meta.model и полями, существующими в вашей модели.

Регистрация обычно требует больше одной формы: constraint уникальности, подтверждение email, rate limit, neutral error policy, согласия и очистка незавершённых аккаунтов. Автоматический login() сразу после создания допустим только если политика продукта не требует подтверждения email или дополнительного фактора.

Ни Django User, ни AbstractUser не предоставляют готовый email verification, MFA, social login или passkeys. Используйте поддерживаемые библиотеки/провайдера и моделируйте восстановление доступа до запуска продукта.

#Legacy и удалённое поведение

LogoutView перестал поддерживать logout через GET в Django 5.0; актуальный путь — POST. Legacy-шаблоны со ссылкой выхода нужно обновить.

Прямой импорт User, таблица auth_user, ручной login из request.POST и собственная password form часто встречаются в старых проектах. Они не всегда являются немедленной уязвимостью, но мешают custom user, обходят validators или создают open redirect. Исправляйте их по одному, закрепляя login/logout/password-reset интеграционными тестами.

#Документация

  • Using the authentication system
  • Customizing authentication
  • Password management
  • Auth settings

Далее: Авторизация: Permissions & Groups