Spec-Zone.ru › Django 4.1

Настройка аутентификации в 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 пользователей и запрашивает встроенные разрешения. Он не предоставляет защиты от атак методом «brute-force» с помощью механизмов ограничения скорости. Вы можете реализовать свой собственный механизм ограничения скорости в пользовательском модуле аутентификации или использовать механизмы, предоставляемые большинством веб-серверов.

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

Вот пример модуля, который выполняет аутентификацию по имени пользователя и паролю, определенным в вашем файле settings.py, и создаёт объект 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)
                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, которую вы хотите использовать в качестве модели пользователя.

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

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

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

is_anonymous

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

set_password(raw_password)

Устанавливает пароль пользователя в заданную строку-сырец, позаботившись о хэшировании пароля. Не сохраняет объект AbstractBaseUser.

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

check_password(raw_password)

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

set_unusable_password()

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

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

has_usable_password()

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

get_session_auth_hash()

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

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)

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

make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')

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

  • i, l, I, и 1 (маленькая буква i, маленькая буква L, большая буква i и цифра один)
  • o, O, и 0 (маленькая буква o, большая буква o и цифра ноль)

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

Если вам полностью подходит модель пользователя 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

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

class models.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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.1/topics/auth/customizing/

Spec-Zone.ru

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