Настройка аутентификации в Django
Встроенная аутентификация Django подходит для большинства стандартных случаев, но иногда могут потребоваться настройки, которых нет в стандартных значениях по умолчанию. Настройка аутентификации в ваших проектах требует понимания, какие части предоставленной системы можно расширить или заменить. Этот документ предоставляет подробную информацию о том, как настроить систему аутентификации.
Модули аутентификации обеспечивают расширяемую систему для случаев, когда для аутентификации имени пользователя и пароля, хранящихся в модели пользователя, необходимо использовать службу, отличную от стандартной Django.
Вы можете задать для своих моделей собственные разрешения, которые могут проверяться через систему авторизации Django.
Вы можете расширить стандартную User модель или заменить её полностью настраиваемой моделью.
Другие источники аутентификации
В некоторых случаях может потребоваться подключение к другому источнику аутентификации — другому источнику имен пользователей и паролей или методам аутентификации.
Например, ваша компания может иметь настроенный LDAP, хранящий имя пользователя и пароль каждого сотрудника. Было бы неудобно как для сетевого администратора, так и для самих пользователей, если бы пользователи имели отдельные учетные записи в LDAP и приложениях, основанных на Django.
Таким образом, для обработки таких ситуаций система аутентификации Django позволяет подключать другие источники аутентификации. Вы можете переопределить стандартную базу данных Django или использовать стандартную систему совместно с другими системами.
См. справочник по модулям аутентификации для получения информации о модулях аутентификации, включённых в Django.
Указание модулей аутентификации
Внутри Django поддерживается список «модулей аутентификации», которые проверяются на аутентификацию. Когда кто-то вызывает django.contrib.auth.authenticate() — как описано в Инструкции по входу пользователя — Django пытается выполнить аутентификацию по всем модулям аутентификации. Если первый метод аутентификации не удался, Django пытается применить второй и так далее, пока не будут испробованы все модули.
Список используемых модулей аутентификации задается в настройке AUTHENTICATION_BACKENDS. Он должен содержать список путей к Python-классам, которые знают, как выполнять аутентификацию. Эти классы могут находиться в любом месте на вашем пути Python.
По умолчанию, AUTHENTICATION_BACKENDS устанавливается в:
["django.contrib.auth.backends.ModelBackend"]
Это основной модуль аутентификации, который проверяет базу данных Django для пользователей и запросы встроенных разрешений. Он не предоставляет защиту от атак методом перебора паролей через механизмы ограничения скорости. Вы можете либо реализовать собственный механизм ограничения скорости в собственном модуле аутентификации, либо использовать механизмы, предоставляемые большинством веб-серверов.
Порядок следования AUTHENTICATION_BACKENDS имеет значение, поэтому если имя пользователя и пароль верны для нескольких модулей, Django прекратит обработку на первой успешной проверке.
Если модуль вызывает исключение PermissionDenied, аутентификация немедленно завершится. Django не будет проверять последующие модули.
Примечание
После аутентификации пользователя Django сохраняет, какой модуль был использован для аутентификации пользователя в сессии и повторно использует этот же модуль в течение этой сессии всякий раз, когда требуется доступ к текущему аутентифицированному пользователю. Это эффективно означает, что источники аутентификации кэшируются на основе сессии, поэтому если вы измените AUTHENTICATION_BACKENDS, вам необходимо очистить данные сессии, если вам нужно принудительно заставить пользователей повторно пройти аутентификацию с использованием разных методов. Простой способ сделать это — выполнить Session.objects.all().delete().
Создание модуля аутентификации
Модуль аутентификации — это класс, который реализует два обязательных метода: get_user(user_id) и authenticate(request, **credentials), а также набор дополнительных методов, связанных с разрешениями методы авторизации.
Метод get_user принимает user_id — который может быть именем пользователя, идентификатором базы данных или чем-либо другим, но должен быть первичным ключом вашего объекта пользователя — и возвращает объект пользователя или None.
Метод authenticate принимает аргумент request и учетные данные в качестве ключевых аргументов. В большинстве случаев это выглядит так:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
Но он также может аутентифицировать токен, например так:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
В любом случае, authenticate() должен проверить полученные учетные данные и вернуть объект пользователя, соответствующий этим учетным данным, если они корректны. Если учетные данные неверны, он должен вернуть None.
request является объектом HttpRequest и может быть None , если он не был предоставлен authenticate() (который передает его в модуль).
Админка Django тесно связана с объектом пользователя Django объект пользователя. Лучший способ справиться с этим — создать объект 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, что позволяет модулю указать поведение авторизации для анонимных пользователей. Это особенно полезно для авторов многократно используемых приложений, которые могут делегировать все вопросы авторизации модулю аутентификации, вместо того, чтобы, например, устанавливать параметры для управления доступом анонимных пользователей.
Авторизация для неактивных пользователей
Вы можете использовать AllowAllUsersModelBackend или AllowAllUsersRemoteUserBackend, если хотите разрешить аутентификацию неактивным пользователям.
Поддержка анонимных пользователей в системе разрешений позволяет реализовать сценарий, в котором анонимные пользователи имеют разрешения на выполнение каких-либо действий, а неактивные авторизованные пользователи — нет.
Не забудьте проверить атрибут is_active пользователя в собственных методах разрешений бэкенда.
Обработка разрешений на объекты
В рамках фреймворка разрешений Django заложена основа для разрешений на объекты, хотя в ядре реализации нет. Это означает, что проверка разрешений на объекты всегда вернёт False или пустой список (в зависимости от проведённой проверки). Бэкенд аутентификации получит ключевые параметры obj и user_obj для каждого метода авторизации, связанного с объектом, и может вернуть разрешение на уровне объекта соответствующим образом.
Настраиваемые разрешения
Для создания настраиваемых разрешений для заданного объекта модели используйте атрибут permissions атрибут модели Meta.
В этом примере Task модель создаёт два настраиваемых разрешения, т. е. действия, которые пользователи могут или не могут выполнять с Task экземплярами, специфичные для вашего приложения:
class Task(models.Model):
...
class Meta:
permissions = [
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
]
Единственное, что это делает, — создаёт эти дополнительные разрешения при запуске manage.py migrate (функция, создающая разрешения, связана с сигналом post_migrate). Ваш код отвечает за проверку значения этих разрешений, когда пользователь пытается получить доступ к функциональности, предоставляемой приложением (изменение состояния задач или закрытие задач). Продолжая пример выше, следующая проверка проверяет, может ли пользователь закрыть задачи:
user.has_perm("app.close_task")
Расширение существующей модели User
Существует два способа расширения модели по умолчанию User без замены собственной модели. Если необходимые изменения носят чисто поведенческий характер и не требуют никаких изменений в хранимых в базе данных данных, вы можете создать модель-прокси на основе User. Это позволяет использовать любые функции, предлагаемые моделями-прокси, включая стандартный порядок сортировки, пользовательских менеджеров или пользовательские методы модели.
Если вы хотите хранить информацию, относящуюся к User, вы можете использовать OneToOneField к модели, содержащей поля для дополнительной информации. Эта модель один-к-одному часто называется моделью профиля, так как она может хранить информацию о пользователе сайта, не связанную с аутентификацией. Например, вы можете создать модель Employee:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
Предполагая, что существует существующий сотрудник Фред Смит, у которого есть как модель User, так и модель Employee, вы можете получить доступ к связанной информации, используя стандартные соглашения Django для связанных моделей:
>>> u = User.objects.get(username="fsmith") >>> freds_department = u.employee.department
Чтобы добавить поля модели профиля на страницу пользователя в админке, определите InlineModelAdmin (в данном примере мы будем использовать StackedInline) в admin.py своего приложения и добавьте его в класс UserAdmin, зарегистрированный с классом User:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = "employee"
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = [EmployeeInline]
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Эти модели профиля ничем особым не являются — это просто модели Django, которые случайно имеют однозначную связь с моделью пользователя. Поэтому они не создаются автоматически при создании пользователя, но для создания или обновления связанных моделей можно использовать django.db.models.signals.post_save.
Использование связанных моделей приводит к дополнительным запросам или соединениям для извлечения связанных данных. В зависимости от ваших потребностей, пользовательская модель, включающая связанные поля, может быть лучшим вариантом, однако существующие связи с моделью пользователя по умолчанию в приложениях вашего проекта могут оправдать дополнительную нагрузку на базу данных.
Замена пользовательской модели User
В некоторых проектах требования к аутентификации могут быть такими, что встроенная модель пользователя Django User не всегда подходит. Например, на некоторых сайтах логичнее использовать адрес электронной почты в качестве маркера идентификации вместо имени пользователя.
Django позволяет переопределить модель пользователя по умолчанию, указав значение настройки AUTH_USER_MODEL, которая ссылается на пользовательскую модель:
AUTH_USER_MODEL = "myapp.MyUser"
Эта пара с точкой описывает label приложения Django (которое должно находиться в вашем списке INSTALLED_APPS), и имя модели Django, которую вы хотите использовать в качестве модели пользователя.
Использование пользовательской модели при создании проекта
Если вы начинаете новый проект, настоятельно рекомендуется настроить пользовательскую модель, даже если модель пользователя по умолчанию 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.
Импорт
AbstractBaseUserAbstractBaseUserиBaseUserManagerимпортируемы изdjango.contrib.auth.base_user, чтобы их можно было импортировать без включенияdjango.contrib.authвINSTALLED_APPS. -
Следующие атрибуты и методы доступны для любого подкласса AbstractBaseUser:
-
class models.AbstractBaseUser -
-
get_username() -
Возвращает значение поля, указанного в
USERNAME_FIELD.
-
clean() -
Нормализует имя пользователя, вызвав
normalize_username(). Если вы переопределяете этот метод, убедитесь, что вызываетеsuper()для сохранения нормализации.
-
classmethod get_email_field_name() -
Возвращает имя поля электронной почты, указанное атрибутом
EMAIL_FIELD. По умолчанию'email', еслиEMAIL_FIELDне указан.
-
classmethod normalize_username(username) -
Применяет нормализацию Unicode NFKC к именам пользователей, чтобы визуально идентичные символы с разными кодами Unicode считались идентичными.
-
is_authenticated -
Только для чтения атрибут, который всегда
True(в отличие отAnonymousUser.is_authenticated, который всегдаFalse). Это способ определить, был ли пользователь аутентифицирован. Это не подразумевает никаких разрешений и не проверяет, активен ли пользователь или у него есть действительная сессия. Хотя обычно вы будете проверять этот атрибут вrequest.user, чтобы узнать, был ли он заполненAuthenticationMiddleware(представляющий текущего вошедшего пользователя), вы должны знать, что этот атрибутTrueдля любой экземпляраUser.
-
is_anonymous -
Только для чтения атрибут, который всегда
False. Это способ различать объектыUserиAnonymousUser. В целом, вы должны предпочесть использоватьis_authenticatedэтому атрибуту.
-
set_password(raw_password) -
Устанавливает пароль пользователя на заданную строку, обрабатывая хеширование пароля. Не сохраняет объект
AbstractBaseUser.Когда raw_password равен
None, пароль будет установлен на недопустимый пароль, как будто используетсяset_unusable_password().
-
check_password(raw_password)
-
acheck_password(raw_password) -
Асинхронная версия:
acheck_password()Возвращает
True, если заданная строка — правильный пароль для пользователя. (Это учитывает хеширование пароля при сравнении.)Изменено в Django 5.0:acheck_password()метод был добавлен.
-
set_unusable_password() -
Помечает пользователя как не имеющего установленного пароля. Это не то же самое, что иметь пустую строку для пароля.
check_password()для этого пользователя никогда не вернетTrue. Не сохраняет объектAbstractBaseUser.Это может понадобиться, если аутентификация для вашего приложения происходит с использованием внешнего источника, такого как каталог LDAP.
-
has_usable_password() -
Возвращает
False, еслиset_unusable_password()был вызван для этого пользователя.
-
get_session_auth_hash() -
Возвращает HMAC поля пароля. Используется для Отмены действия сеанса при изменении пароля.
-
get_session_auth_fallback_hash() -
Новое в Django 4.1.8.
Возвращает HMAC поля пароля, используя
SECRET_KEY_FALLBACKS. Используется вget_user().
-
AbstractUser наследуется от AbstractBaseUser:
-
class models.AbstractUser -
-
clean() -
Нормализует адрес электронной почты, вызвав
BaseUserManager.normalize_email(). Если вы переопределяете этот метод, убедитесь, что вызываетеsuper()для сохранения нормализации.
-
Создание менеджера для пользовательской модели
Вы также должны определить пользовательского менеджера для своей модели пользователя. Если ваша модель пользователя определяет поля username, email, is_staff, is_active, is_superuser, last_login, и date_joined так же, как у стандартной модели Django, вы можете использовать менеджер 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') -
Устарело начиная с версии 4.2.
Возвращает случайный пароль заданной длины и с заданным набором разрешенных символов. Обратите внимание, что значение по умолчанию
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. SetPasswordFormPasswordChangeFormAdminPasswordChangeForm
Следующие формы делают предположения о модели пользователя и могут использоваться как есть, если эти предположения верны:
-
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",)
В более старых версиях, UserCreationForm не сохраняла многие-ко-многим поля формы для пользовательской модели.
Пользователи и 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.
-
Пользователи и модели-прокси
Одно ограничение моделей пользователей — при установке пользовательской модели будут нарушаться любые модели-прокси, расширяющие 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/5.0/topics/auth/customizing/