Spec-Zone.ru › Django 5.2

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

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

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

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

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

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

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

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

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

См. справочник по модулям аутентификации для получения информации о модулях аутентификации, включённых в Django.

Указание модулей аутентификации

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

Список используемых модулей аутентификации указан в настройке AUTHENTICATION_BACKENDS. Это должен быть список путей к 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()).

Лучший способ решения этой проблемы — создать объект Django User для каждого пользователя, который существует для вашего модуля (например, в вашем каталоге LDAP, вашей внешней базе данных SQL и т.д.). Вы можете либо написать скрипт для этого заранее, либо метод 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)

Предполагая, что существует существующий сотрудник Фред Смит, у которого есть как модель 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() [source]

Вместо того, чтобы ссылаться на 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 (представляющий текущего вошедшего пользователя), вы должны знать, что этот атрибут является True для любой User экземпляра.

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, но хотите добавить дополнительную информацию о профиле, вы можете унаследовать от 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

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

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/5.2/topics/auth/customizing/

Spec-Zone.ru

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