Spec-Zone.ru › Django 3.1

Настройка аутентификации в 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 объект пользователя. Лучший способ справиться с этим — создать объект 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)

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

Эта строка с точками описывает имя приложения 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 была изменена на другую модель пользователя.

END_OF_DOCUMENT_MARKER ```
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(**kwargs):
    if kwargs['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, таких как создание пользователя в админке.

Новое в Django 3.0:

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

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

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 поля пароля. Используется для Инвалидации сессии при смене пароля.

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

Алгоритм хеширования был изменен на SHA-256.

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)
Новое в Django 3.0.

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

Если 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, если у пользователя есть каждое из указанных разрешений, где каждое perm имеет формат "<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
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


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

Spec-Zone.ru

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