Настройка аутентификации в 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
(строчная буква о, заглавная буква о и ноль)
-
classmethod
Расширение Django по умолчанию User
¶
Если вас полностью устраивает модель Django User
, но вы хотите добавить некоторую дополнительную информацию о профиле, вы можете подкласс django.contrib.auth.models.AbstractUser
и добавить свои пользовательские поля профиля, хотя мы бы рекомендовали создать отдельную модель, как описано в примечании «Соображения по дизайну модели» в Указание пользовательской модели пользователя. AbstractUser
предоставляет полную реализацию стандартного User
в виде abstract model.
Пользовательские пользователи и встроенные формы авторизации¶
Встроенные в Django forms и views делают определенные предположения о модели пользователя, с которой они работают.
Следующие формы совместимы с любым подклассом AbstractBaseUser
:
AuthenticationForm
: Используется поле имени пользователя, указанноеUSERNAME_FIELD
.SetPasswordForm
PasswordChangeForm
AdminPasswordChangeForm
Следующие формы делают предположения о модели пользователя и могут использоваться как есть, если эти предположения выполняются:
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
.
-
Пользовательские пользователи и модели прокси¶
Одним из ограничений пользовательских моделей является то, что установка пользовательской модели пользователя сломает любую прокси-модель, расширяющую 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'
В старых версиях по умолчанию ReadOnlyPasswordHashField
не является disabled
, а UserChangeForm.clean_password()
обязан возвращать начальное значение, какое бы пользователь ни предоставил.