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

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

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

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

Вы можете extend модель по умолчанию User, или substitute полностью настроенную модель.

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

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

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

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

Смотрите authentication backend reference для получения информации о бэкендах аутентификации, включенных в Django.

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

За кулисами Django ведет список «бэкендов аутентификации», которые он проверяет на предмет аутентификации. Когда кто-то вызывает django.contrib.auth.authenticate() - как описано в How to log a user in - 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), а также набор необязательных методов, связанных с разрешениями authorization methods.

Метод get_user принимает user_id - который может быть именем пользователя, идентификатором базы данных или чем угодно, но должен быть первичным ключом вашего объекта user - и возвращает объект user или 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 object. Лучший способ справиться с этим - создать объект 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, все функции backend auth принимают в качестве аргумента объект user, который может быть анонимным пользователем.

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

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

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

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

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

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

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

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

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

Работа с разрешениями объектов

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

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

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

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

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

Если вы начинаете новый проект, настоятельно рекомендуется установить пользовательскую модель пользователя, даже если вам достаточно модели по умолчанию 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(**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

Строка, описывающая имя поля email в модели 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)

Применяет нормализацию NFKC Unicode к именам пользователей, чтобы визуально идентичные символы с разными кодовыми точками 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 (строчная буква о, заглавная буква о и ноль)

Расширение Django по умолчанию User

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

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

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

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

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

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

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

Если ваша пользовательская модель пользователя является подклассом 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',)}),
    )

Более подробную информацию см. в разделе a full example.

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

Чтобы упростить включение структуры разрешений 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>" (см. permissions). Если 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'
Changed in Django 3.2:

В старых версиях по умолчанию ReadOnlyPasswordHashField не является disabled, а UserChangeForm.clean_password() обязан возвращать начальное значение, какое бы пользователь ни предоставил.

Вернуться на верх