Spec-Zone.ru › Django 6.0

Настройка аутентификации в Django

Аутентификация, входящая в состав Django, подходит для большинства распространённых случаев, но ваши потребности могут не соответствовать настройкам по умолчанию. Чтобы настроить аутентификацию в своих проектах, нужно понимать, какие части предоставленной системы можно расширить или заменить. В этом документе подробно описано, как можно настроить систему аутентификации.

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

Вы можете назначить своим моделям пользовательские разрешения, которые можно проверять с помощью системы авторизации Django.

Вы можете расширить стандартную модель User или заменить её полностью настроенной моделью.

Другие источники аутентификации

Иногда возникает необходимость подключить другой источник аутентификации — то есть другой источник имён пользователей и паролей или других методов аутентификации.

Например, в вашей компании уже может быть настроен LDAP, в котором хранятся имя пользователя и пароль каждого сотрудника. Наличие отдельных учётных записей в LDAP и приложениях на Django стало бы обременительным как для сетевого администратора, так и для самих пользователей.

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

Информацию о бэкендах аутентификации, входящих в состав Django, см. в справочнике по бэкендам аутентификации.

Указание бэкендов аутентификации

Внутри Django поддерживает список «бэкендов аутентификации», которые проверяются при аутентификации. Когда кто-либо вызывает django.contrib.auth.authenticate() — как описано в разделе Как выполнить вход пользователя, — Django пытается выполнить аутентификацию с помощью всех своих бэкендов. Если первый метод аутентификации не срабатывает, Django пробует второй и так далее, пока не проверит все бэкенды.

Список используемых бэкендов аутентификации задаётся параметром AUTHENTICATION_BACKENDS. Это должен быть список путей Python, указывающих на классы Python, которые умеют выполнять аутентификацию. Эти классы могут находиться где угодно в пути Python.

По умолчанию параметру AUTHENTICATION_BACKENDS присваивается значение:

["django.contrib.auth.backends.ModelBackend"]

Это базовый бэкенд аутентификации, который проверяет базу данных пользователей Django и выполняет запросы к встроенным разрешениям. Он не обеспечивает защиту от атак методом перебора с помощью ограничения частоты запросов. Вы можете реализовать собственный механизм ограничения частоты запросов в пользовательском бэкенде аутентификации или использовать механизмы, предоставляемые большинством веб-серверов.

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

Если бэкенд вызывает исключение PermissionDenied, аутентификация немедленно завершится неудачей. Django не будет проверять следующие бэкенды.

Примечание

После успешной аутентификации пользователя Django сохраняет в его сеансе сведения о том, какой бэкенд использовался, и повторно использует тот же бэкенд в течение всего сеанса, когда требуется доступ к текущему аутентифицированному пользователю. Это фактически означает, что источники аутентификации кэшируются отдельно для каждого сеанса. Поэтому при изменении AUTHENTICATION_BACKENDS, если вы хотите заставить пользователей пройти аутентификацию другими способами, потребуется очистить данные сеансов. Для этого можно выполнить Session.objects.all().delete().

Создание бэкенда аутентификации

Бэкенд аутентификации — это класс, реализующий два обязательных метода: get_user(user_id) и authenticate(request, **credentials), а также набор необязательных методов авторизации, связанных с разрешениями.

Метод get_user принимает user_id — это может быть имя пользователя, идентификатор в базе данных или что-либо ещё, но он должен быть первичным ключом объекта пользователя — и возвращает объект пользователя или None.

Метод authenticate принимает аргумент request и учётные данные в виде именованных аргументов. Чаще всего он выглядит так:

from django.contrib.auth.backends import BaseBackend


class MyBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        # Check the username/password and return a user.
        ...

Однако он также может выполнять аутентификацию по токену, например:

from django.contrib.auth.backends import BaseBackend


class MyBackend(BaseBackend):
    def authenticate(self, request, token=None):
        # Check the token and return a user.
        ...

В любом случае authenticate() должен проверять полученные учётные данные и возвращать соответствующий им объект пользователя, если они действительны. Если они недействительны, метод должен возвращать None.

request — это объект HttpRequest, который может иметь значение None, если он не был передан в authenticate() (который передаёт его бэкенду).

Панель администратора Django тесно связана с объектом пользователя Django. Например, чтобы пользователь мог получить доступ к панели администратора, значения User.is_staff и User.is_active должны быть равны True (подробности см. в AdminSite.has_permission()).

Лучше всего для каждого пользователя, существующего в вашем бэкенде (например, в каталоге LDAP или во внешней базе данных SQL), создать объект Django User. Вы можете заранее написать для этого скрипт или создать его в методе authenticate при первом входе пользователя в систему.

Ниже приведён пример бэкенда, который выполняет аутентификацию по имени пользователя и паролю, заданным в файле settings.py, и при первой успешной аутентификации пользователя создаёт объект Django User. В этом примере созданный объект Django User является суперпользователем, который получит полный доступ к панели администратора:

from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User


class SettingsBackend(BaseBackend):
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
    """

    def authenticate(self, request, username=None, password=None):
        login_valid = settings.ADMIN_LOGIN == username
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. There's no need to set a password
                # because only the password from settings.py is checked.
                user = User(username=username)  # is_active defaults to True.
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Обработка авторизации в пользовательских бэкендах

Пользовательские бэкенды аутентификации могут предоставлять собственные разрешения.

Модель пользователя и её менеджер делегируют функции поиска разрешений (get_user_permissions(), get_group_permissions(), get_all_permissions(), has_perm(), has_module_perms() и with_perm()) любым бэкендам аутентификации, реализующим эти функции.

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

Если бэкенд вызывает исключение PermissionDenied в методе has_perm() или has_module_perms(), авторизация немедленно завершится неудачей, и Django не будет проверять следующие бэкенды.

Бэкенд может реализовать разрешения для панели администратора следующим образом:

from django.contrib.auth.backends import BaseBackend


class MagicAdminBackend(BaseBackend):
    def has_perm(self, user_obj, perm, obj=None):
        return user_obj.username == settings.ADMIN_LOGIN

Это предоставляет все разрешения пользователю, которому разрешён доступ в приведённом выше примере. Обратите внимание: помимо тех же аргументов, что и у соответствующих функций django.contrib.auth.models.User, функции авторизации бэкенда принимают объект пользователя в качестве аргумента; этим объектом может быть анонимный пользователь.

Полную реализацию авторизации можно найти в классе ModelBackend в файле django/contrib/auth/backends.py. Это бэкенд, используемый по умолчанию, который в большинстве случаев выполняет запросы к таблице auth_permission.

Авторизация анонимных пользователей

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

В системе разрешений Django нет места для хранения разрешений анонимных пользователей. Однако объект пользователя, переданный бэкенду аутентификации, может быть объектом django.contrib.auth.models.AnonymousUser, что позволяет бэкенду задавать особое поведение авторизации для анонимных пользователей. Это особенно полезно авторам повторно используемых приложений: они могут делегировать все вопросы авторизации бэкенду аутентификации, не добавляя, например, настройки для управления анонимным доступом.

Авторизация неактивных пользователей

Неактивный пользователь — это пользователь, у которого поле is_active имеет значение False. Бэкенды аутентификации ModelBackend и RemoteUserBackend запрещают таким пользователям проходить аутентификацию. Если в пользовательской модели пользователя нет поля is_active, проходить аутентификацию смогут все пользователи.

Если вы хотите разрешить аутентификацию неактивным пользователям, можно использовать AllowAllUsersModelBackend или AllowAllUsersRemoteUserBackend.

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

Не забудьте проверять атрибут is_active пользователя в собственных методах разрешений бэкенда.

Обработка разрешений объектов

В основе системы разрешений Django предусмотрена поддержка разрешений объектов, но в ядре отсутствует её реализация. Это означает, что проверка разрешений объектов всегда будет возвращать False или пустой список (в зависимости от выполняемой проверки). Бэкенд аутентификации получает именованные параметры obj и user_obj для каждого метода авторизации, связанного с объектом, и может возвращать соответствующее разрешение на уровне объекта.

Пользовательские разрешения

Чтобы создать пользовательские разрешения для объекта определённой модели, используйте permissions — атрибут Meta модели.

В этом примере модель Task создаёт два пользовательских разрешения — то есть действия, которые пользователи могут или не могут выполнять с экземплярами Task, характерные для вашего приложения:

class Task(models.Model):
    ...

    class Meta:
        permissions = [
            ("change_task_status", "Can change the status of tasks"),
            ("close_task", "Can remove a task by setting its status as closed"),
        ]

Это лишь создаёт дополнительные разрешения при запуске manage.py migrate (функция создания разрешений подключена к сигналу post_migrate). Ваш код должен проверять значение этих разрешений, когда пользователь пытается получить доступ к функциональности приложения (изменить статус задач или закрыть задачи). Продолжая приведённый выше пример, следующий код проверяет, может ли пользователь закрывать задачи:

user.has_perm("app.close_task")

Расширение существующей модели User

Существует два способа расширить стандартную модель User, не заменяя её собственной моделью. Если требуемые изменения касаются только поведения и не требуют менять данные, хранящиеся в базе данных, можно создать прокси-модель на основе User. Это позволяет использовать любые возможности прокси-моделей, включая сортировку по умолчанию, пользовательские менеджеры и пользовательские методы модели.

Если вы хотите хранить информацию, связанную с User, можно использовать OneToOneField для связи с моделью, содержащей дополнительные поля. Такую модель «один к одному» часто называют моделью профиля, поскольку в ней может храниться информация о пользователе сайта, не относящаяся к аутентификации. Например, можно создать модель Employee:

from django.contrib.auth.models import User


class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)

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

>>> u = User.objects.get(username="fsmith")
>>> freds_department = u.employee.department

Чтобы добавить поля модели профиля на страницу пользователя в панели администратора, определите InlineModelAdmin (в этом примере мы используем StackedInline) в файле admin.py вашего приложения и добавьте его в класс UserAdmin, зарегистрированный для класса User:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import Employee


# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
    model = Employee
    can_delete = False
    verbose_name_plural = "employee"


# Define a new User admin
class UserAdmin(BaseUserAdmin):
    inlines = [EmployeeInline]


# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Эти модели профилей не обладают какими-либо особыми свойствами — это обычные модели Django, связанные с моделью пользователя отношением «один к одному». Поэтому они не создаются автоматически при создании пользователя, однако для создания или обновления связанных моделей при необходимости можно использовать сигнал django.db.models.signals.post_save.

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

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

Для некоторых типов проектов могут существовать требования к аутентификации, которым встроенная модель Django User не всегда соответствует. Например, на некоторых сайтах в качестве идентификатора удобнее использовать адрес электронной почты, а не имя пользователя.

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

AUTH_USER_MODEL = "myapp.MyUser"

Эта пара, разделённая точкой, описывает label приложения Django (которое должно быть в INSTALLED_APPS) и имя модели Django, которую вы хотите использовать в качестве пользовательской модели.

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

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

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    pass

Не забудьте указать её в параметре AUTH_USER_MODEL. Сделайте это до создания миграций или первого запуска manage.py migrate.

Также зарегистрируйте модель в файле admin.py приложения:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

Переход на пользовательскую модель в ходе проекта

Изменить AUTH_USER_MODEL после создания таблиц базы данных возможно, но это может быть сложно, поскольку, например, затрагиваются внешние ключи и связи «многие ко многим».

Это изменение нельзя выполнить автоматически: потребуется вручную исправить схему, перенести данные из старой таблицы пользователей и, возможно, вручную повторно применить некоторые миграции. Общий план действий приведён в #25313.

Из-за ограничений динамических зависимостей Django для заменяемых моделей модель, указанная в AUTH_USER_MODEL, должна создаваться в первой миграции своего приложения (обычно называемой 0001_initial); в противном случае возникнут проблемы с зависимостями.

Кроме того, при выполнении миграций может возникнуть CircularDependencyError, поскольку из-за динамической зависимости Django не сможет автоматически разорвать цикл зависимостей. Если вы увидите эту ошибку, разорвите цикл, переместив модели, от которых зависит ваша пользовательская модель, во вторую миграцию. (Если хотите посмотреть, как обычно разрешается эта циклическая зависимость, можно попробовать создать две обычные модели со связью ForeignKey друг с другом и посмотреть, как makemigrations её разрешает.)

Повторно используемые приложения и AUTH_USER_MODEL

Повторно используемые приложения не должны реализовывать пользовательскую модель. В проекте может использоваться множество приложений, и два повторно используемых приложения, реализующих пользовательскую модель, нельзя будет использовать вместе. Если приложению нужно хранить информацию о пользователе, используйте ForeignKey или OneToOneField для settings.AUTH_USER_MODEL, как описано ниже.

Ссылки на модель User

Если вы напрямую ссылаетесь на User (например, указывая её во внешнем ключе), ваш код не будет работать в проектах, где параметр AUTH_USER_MODEL изменён и указывает на другую пользовательскую модель.

get_user_model() [исходный код]

Вместо прямой ссылки на User следует ссылаться на пользовательскую модель с помощью django.contrib.auth.get_user_model(). Этот метод возвращает текущую активную пользовательскую модель — пользовательскую модель, если она указана, или User в противном случае.

При определении внешнего ключа или связи «многие ко многим» с пользовательской моделью следует указать пользовательскую модель с помощью параметра AUTH_USER_MODEL. Например:

from django.conf import settings
from django.db import models


class Article(models.Model):
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

При подключении к сигналам, отправляемым пользовательской моделью, следует указать пользовательскую модель с помощью параметра AUTH_USER_MODEL. Например:

from django.conf import settings
from django.db.models.signals import post_save


def post_save_receiver(sender, instance, created, **kwargs):
    pass


post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)

Как правило, проще всего ссылаться на пользовательскую модель с помощью параметра AUTH_USER_MODEL в коде, выполняемом во время импорта. Однако можно также вызвать get_user_model() во время импорта моделей Django, поэтому можно использовать models.ForeignKey(get_user_model(), ...).

Если ваше приложение тестируется с несколькими пользовательскими моделями, например с использованием @override_settings(AUTH_USER_MODEL=...), и результат get_user_model() кэшируется в переменной уровня модуля, может потребоваться обрабатывать сигнал setting_changed, чтобы очищать кэш. Например:

from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.signals import setting_changed
from django.dispatch import receiver


@receiver(setting_changed)
def user_model_swapped(*, setting, **kwargs):
    if setting == "AUTH_USER_MODEL":
        apps.clear_cache()
        from myapp import some_module

        some_module.UserModel = get_user_model()

Выбор пользовательской модели

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

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

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

Самый простой способ создать совместимую пользовательскую модель — унаследовать её от AbstractBaseUser. AbstractBaseUser предоставляет основную реализацию пользовательской модели, включая хеширование паролей и сброс паролей с помощью токенов. Затем необходимо указать некоторые ключевые детали реализации:

class models.CustomUser
USERNAME_FIELD

Строка с именем поля пользовательской модели, используемого в качестве уникального идентификатора. Обычно это имя пользователя, но им также может быть адрес электронной почты или любой другой уникальный идентификатор. Поле должно быть уникальным (например, в его определении должно быть задано unique=True), если только вы не используете пользовательскую серверную часть аутентификации, поддерживающую неуникальные имена пользователей.

В следующем примере для идентификации используется поле identifier:

class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = "identifier"
EMAIL_FIELD

Строка с именем поля адреса электронной почты модели User. Это значение возвращается методом get_email_field_name().

REQUIRED_FIELDS

Список имён полей, значения которых запрашиваются при создании пользователя с помощью команды управления createsuperuser. Пользователю будет предложено ввести значение для каждого из этих полей. Список должен включать все поля, у которых blank имеет значение False или не задано, а также может включать дополнительные поля, значения которых требуется запрашивать при интерактивном создании пользователя. REQUIRED_FIELDS не влияет на другие части Django, например на создание пользователя в административной панели.

Например, ниже приведено частичное определение пользовательской модели с двумя обязательными полями — датой рождения и ростом:

class MyUser(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ["date_of_birth", "height"]

Примечание

REQUIRED_FIELDS должен содержать все обязательные поля вашей пользовательской модели, но не должен содержать USERNAME_FIELD или password, поскольку значения этих полей запрашиваются всегда.

is_active

Логический атрибут, указывающий, считается ли пользователь «активным». Этот атрибут предоставляется в AbstractBaseUser и по умолчанию имеет значение True. Способ его реализации зависит от особенностей выбранных серверных частей аутентификации. Подробности см. в документации атрибута is_active attribute on the built-in user model.

get_full_name()

Необязательный метод. Более полное официальное обозначение пользователя, например его полное имя. Если метод реализован, это обозначение отображается рядом с именем пользователя в истории объекта в django.contrib.admin.

get_short_name()

Необязательный метод. Краткое неофициальное обозначение пользователя, например его имя. Если метод реализован, оно заменяет имя пользователя в приветствии в заголовке django.contrib.admin.

Импорт AbstractBaseUser

AbstractBaseUser и BaseUserManager можно импортировать из django.contrib.auth.base_user, не добавляя django.contrib.auth в INSTALLED_APPS.

Следующие атрибуты и методы доступны в любом подклассе AbstractBaseUser:

class models.AbstractBaseUser
get_username()

Возвращает значение поля, указанного в USERNAME_FIELD.

clean()

Нормализует имя пользователя, вызывая normalize_username(). Если вы переопределяете этот метод, обязательно вызовите super(), чтобы сохранить нормализацию.

classmethod get_email_field_name()

Возвращает имя поля электронной почты, указанное в атрибуте EMAIL_FIELD. По умолчанию возвращает 'email', если EMAIL_FIELD не указан.

classmethod normalize_username(username)

Применяет к именам пользователей нормализацию Unicode NFKC, чтобы визуально одинаковые символы с разными кодовыми точками Unicode считались одинаковыми.

is_authenticated

Атрибут только для чтения, значение которого всегда равно True (в отличие от AnonymousUser.is_authenticated, значение которого всегда равно False). Он позволяет определить, прошёл ли пользователь аутентификацию. Это не означает наличия каких-либо разрешений и не проверяет, активен ли пользователь и есть ли у него действительный сеанс. Обычно этот атрибут проверяют у request.user, чтобы выяснить, был ли объект заполнен промежуточным слоем AuthenticationMiddleware (представляющим вошедшего в систему пользователя), однако следует учитывать, что для любого экземпляра User этот атрибут имеет значение True.

is_anonymous

Атрибут только для чтения, значение которого всегда равно False. Он позволяет отличать объекты User от объектов AnonymousUser. Как правило, вместо этого атрибута рекомендуется использовать is_authenticated.

set_password(raw_password)

Устанавливает пользователю пароль из заданной строки в открытом виде, выполняя необходимое хеширование. Объект AbstractBaseUser не сохраняется.

Если raw_password имеет значение None, будет установлен непригодный для использования пароль, как если бы был вызван метод set_unusable_password().

check_password(raw_password)
acheck_password(raw_password)

Асинхронная версия: acheck_password()

Возвращает True, если заданная строка в открытом виде является правильным паролем пользователя. (При сравнении выполняется необходимое хеширование пароля.)

set_unusable_password()

Отмечает, что у пользователя не установлен пароль. Это не то же самое, что пустая строка в качестве пароля. Метод check_password() для этого пользователя никогда не вернёт True. Объект AbstractBaseUser не сохраняется.

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

has_usable_password()

Возвращает False, если для этого пользователя был вызван метод set_unusable_password().

get_session_auth_hash()

Возвращает HMAC поля пароля. Используется для инвалидации сеанса при смене пароля.

get_session_auth_fallback_hash()

Возвращает HMAC поля пароля, используя SECRET_KEY_FALLBACKS. Используется методом get_user().

AbstractUser является подклассом AbstractBaseUser:

class models.AbstractUser
clean()

Нормализует адрес электронной почты, вызывая BaseUserManager.normalize_email(). Если вы переопределяете этот метод, обязательно вызовите super(), чтобы сохранить нормализацию.

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

Для пользовательской модели также следует определить пользовательский менеджер. Если ваша пользовательская модель содержит поля username, email, is_staff, is_active, is_superuser, last_login и date_joined, совпадающие с полями пользователя Django по умолчанию, можно установить менеджер Django UserManager. Если же ваша пользовательская модель содержит другие поля, потребуется определить пользовательский менеджер, унаследованный от BaseUserManager, с двумя дополнительными методами:

class models.CustomUserManager
create_user(username_field, password=None, **other_fields)

В сигнатуре create_user() должны присутствовать поле имени пользователя и все обязательные поля в качестве аргументов. Например, если в пользовательской модели в качестве поля имени пользователя используется email, а date_of_birth является обязательным полем, метод create_user следует определить так:

def create_user(self, email, date_of_birth, password=None):
    # create user here
    ...
create_superuser(username_field, password=None, **other_fields)

В сигнатуре create_superuser() должны присутствовать поле имени пользователя и все обязательные поля в качестве аргументов. Например, если в пользовательской модели в качестве поля имени пользователя используется email, а date_of_birth является обязательным полем, метод create_superuser следует определить так:

def create_superuser(self, email, date_of_birth, password=None):
    # create superuser here
    ...

Если ForeignKey используется в USERNAME_FIELD или REQUIRED_FIELDS, эти методы получают значение to_field (по умолчанию — primary_key) существующего экземпляра.

BaseUserManager предоставляет следующие вспомогательные методы:

class models.BaseUserManager
classmethod normalize_email(email)

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

get_by_natural_key(username)
aget_by_natural_key(username)

Асинхронная версия: aget_by_natural_key()

Получает экземпляр пользователя, используя содержимое поля, указанного в USERNAME_FIELD.

Изменено в Django 5.2:

Добавлен метод aget_by_natural_key().

Расширение стандартной модели пользователя Django User

Если вас полностью устраивает модель Django User, но вы хотите добавить дополнительные сведения о профиле, можно унаследовать django.contrib.auth.models.AbstractUser и добавить собственные поля профиля, хотя мы рекомендуем использовать отдельную модель, как описано в разделе Выбор пользовательской модели. AbstractUser содержит полную реализацию стандартной модели User в виде абстрактной модели.

Пользовательские модели и встроенные формы аутентификации

Встроенные в Django формы и представления делают определённые предположения об используемой пользовательской модели.

Следующие формы совместимы с любым подклассом AbstractBaseUser:

  • AuthenticationForm: использует поле имени пользователя, указанное в USERNAME_FIELD.
  • SetPasswordForm
  • PasswordChangeForm
  • AdminPasswordChangeForm

Следующие формы делают определённые предположения о пользовательской модели и могут использоваться без изменений, если эти предположения выполняются:

  • PasswordResetForm: предполагает, что в пользовательской модели есть поле для адреса электронной почты пользователя с именем, возвращаемым методом get_email_field_name() (по умолчанию — email), которое можно использовать для идентификации пользователя, а также логическое поле с именем is_active, запрещающее сброс пароля неактивными пользователями.

Наконец, следующие формы связаны с User, поэтому для работы с пользовательской моделью их необходимо переписать или расширить:

  • UserCreationForm
  • UserChangeForm

Если ваша пользовательская модель является подклассом AbstractUser, можно расширить эти формы следующим образом:

from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser


class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ("custom_field",)

Пользовательские модели пользователей и django.contrib.admin

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

classmodels.CustomUser
is_staff

Возвращает True, если пользователю разрешен доступ к административному сайту.

is_active

Возвращает True, если учетная запись пользователя активна в данный момент.

has_perm(perm, obj=None):

Возвращает True, если у пользователя есть указанное разрешение. Если задан obj, разрешение необходимо проверить для определенного экземпляра объекта.

has_module_perms(app_label):

Возвращает True, если у пользователя есть разрешение на доступ к моделям указанного приложения.

Также необходимо зарегистрировать пользовательскую модель пользователя в административном сайте. Если ваша пользовательская модель пользователя наследуется от django.contrib.auth.models.AbstractUser, можно использовать существующий класс Django django.contrib.auth.admin.UserAdmin. Однако если ваша модель пользователя наследуется от AbstractBaseUser, потребуется определить пользовательский класс ModelAdmin. Возможно, удастся унаследоваться от стандартного django.contrib.auth.admin.UserAdmin; однако потребуется переопределить все определения, которые ссылаются на поля в django.contrib.auth.models.AbstractUser, отсутствующие в вашем пользовательском классе пользователя.

Примечание

Если вы используете пользовательский ModelAdmin, являющийся подклассом django.contrib.auth.admin.UserAdmin, необходимо добавить пользовательские поля в fieldsets (для полей, используемых при редактировании пользователей) и в add_fieldsets (для полей, используемых при создании пользователя). Например:

from django.contrib.auth.admin import UserAdmin


class CustomUserAdmin(UserAdmin):
    ...
    fieldsets = UserAdmin.fieldsets + ((None, {"fields": ["custom_field"]}),)
    add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ["custom_field"]}),)

Подробнее см. полный пример.

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

Чтобы упростить включение системы разрешений Django в собственный класс пользователя, Django предоставляет PermissionsMixin. Это абстрактная модель, которую можно включить в иерархию классов модели пользователя. Она предоставляет все методы и поля базы данных, необходимые для поддержки системы разрешений Django.

PermissionsMixin предоставляет следующие методы и атрибуты:

class models.PermissionsMixin
is_superuser

Логическое значение. Указывает, что у пользователя есть все разрешения без необходимости назначать их явно.

get_user_permissions(obj=None)

Возвращает набор строк разрешений, непосредственно имеющихся у пользователя.

Если передан obj, возвращаются только разрешения пользователя для этого конкретного объекта.

get_group_permissions(obj=None)

Возвращает набор строк разрешений, имеющихся у пользователя благодаря его группам.

Если передан obj, возвращаются только разрешения групп для этого конкретного объекта.

get_all_permissions(obj=None)

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

Если передан obj, возвращаются только разрешения для этого конкретного объекта.

has_perm(perm, obj=None)

Возвращает True, если у пользователя есть указанное разрешение, где perm имеет формат "<app label>.<permission codename>" (см. разрешения). Если User.is_active и is_superuser одновременно равны True, этот метод всегда возвращает True.

Если передан obj, этот метод проверяет разрешение не для модели, а для конкретного объекта.

has_perms(perm_list, obj=None)

Возвращает True, если у пользователя есть каждое из указанных разрешений, где каждое разрешение имеет формат "<app label>.<permission codename>". Если User.is_active и is_superuser одновременно равны True, этот метод всегда возвращает True.

Если передан obj, этот метод проверяет разрешения не для модели, а для конкретного объекта.

has_module_perms(package_name)

Возвращает True, если у пользователя есть какие-либо разрешения в указанном пакете (метке приложения Django). Если User.is_active и is_superuser одновременно равны True, этот метод всегда возвращает True.

PermissionsMixin и ModelBackend

Если вы не включаете PermissionsMixin, необходимо убедиться, что вы не вызываете методы проверки разрешений в ModelBackend. ModelBackend предполагает наличие в модели пользователя определенных полей. Если модель пользователя не предоставляет эти поля, при проверке разрешений возникнут ошибки базы данных.

Пользовательские модели пользователей и прокси-модели

Одно из ограничений пользовательских моделей пользователей заключается в том, что установка такой модели нарушает работу всех прокси-моделей, наследуемых от User. Прокси-модели должны основываться на конкретном базовом классе; определяя пользовательскую модель пользователя, вы лишаете Django возможности надежно определить базовый класс.

Если в проекте используются прокси-модели, необходимо либо изменить прокси-модель, чтобы она наследовалась от модели пользователя, используемой в проекте, либо объединить поведение прокси-модели с подклассом User.

Полный пример

Вот пример приложения с пользовательской моделью пользователя, совместимой с административным сайтом. В этой модели в качестве имени пользователя используется адрес электронной почты, а дата рождения является обязательной; проверка разрешений не реализована, за исключением флага admin в учетной записи пользователя. Эта модель совместима со всеми встроенными формами и представлениями аутентификации, за исключением форм создания пользователей. Пример показывает, как взаимодействуют большинство компонентов, но его не следует напрямую копировать в проекты для использования в рабочей среде.

Весь этот код будет находиться в файле models.py пользовательского приложения аутентификации:

from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError("Users must have an email address")

        user = self.model(
            email=self.normalize_email(email),
            date_of_birth=date_of_birth,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, date_of_birth, password=None):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        user = self.create_user(
            email,
            password=password,
            date_of_birth=date_of_birth,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser):
    email = models.EmailField(
        verbose_name="email address",
        max_length=255,
        unique=True,
    )
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["date_of_birth"]

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin

Затем, чтобы зарегистрировать эту пользовательскую модель пользователя в административном сайте Django, в файл admin.py приложения нужно добавить следующий код:

from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError

from customauth.models import MyUser


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""

    password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
    password2 = forms.CharField(
        label="Password confirmation", widget=forms.PasswordInput
    )

    class Meta:
        model = MyUser
        fields = ["email", "date_of_birth"]

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    disabled password hash display field.
    """

    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ["email", "password", "date_of_birth", "is_active", "is_admin"]


class UserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ["email", "date_of_birth", "is_admin"]
    list_filter = ["is_admin"]
    fieldsets = [
        (None, {"fields": ["email", "password"]}),
        ("Personal info", {"fields": ["date_of_birth"]}),
        ("Permissions", {"fields": ["is_admin"]}),
    ]
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = [
        (
            None,
            {
                "classes": ["wide"],
                "fields": ["email", "date_of_birth", "password1", "password2"],
            },
        ),
    ]
    search_fields = ["email"]
    ordering = ["email"]
    filter_horizontal = []


# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

Наконец, укажите пользовательскую модель в качестве модели пользователя по умолчанию для проекта с помощью параметра AUTH_USER_MODEL в файле settings.py:

AUTH_USER_MODEL = "customauth.MyUser"

Добавление асинхронного интерфейса

Добавлено в Django 5.2.

Чтобы оптимизировать производительность при вызове из асинхронного контекста аутентификации, бэкенды могут реализовать асинхронные версии каждой функции — aget_user(user_id) и aauthenticate(request, **credentials). Если бэкенд аутентификации наследуется от BaseBackend и асинхронные версии этих функций не предоставлены, они будут автоматически созданы с помощью sync_to_async. Это снижает производительность.

Хотя асинхронный интерфейс необязателен, синхронный интерфейс всегда необходим. Если реализован асинхронный интерфейс, синхронный интерфейс автоматически не создается.

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

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/auth/customizing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API