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

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

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

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

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

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

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

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

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

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

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

За кулисами Django поддерживает список «бэкэндов аутентификации», которые он проверяет на предмет аутентификации. Когда кто-то звонит django.contrib.auth.authenticate()- как описано в разделе «Как войти в систему» - Django пытается пройти аутентификацию во всех своих бэкэндах аутентификации. Если первый метод аутентификации не работает, Django пробует второй, и так далее, пока все бэкенды не будут выполнены.

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

По умолчанию AUTHENTICATION_BACKENDSустановлено:

['django.contrib.auth.backends.ModelBackend']

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

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

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

Примечание

После аутентификации пользователя Django сохраняет, какой бэкэнд использовался для аутентификации пользователя в сеансе пользователя, и повторно использует тот же бэкэнд в течение этого сеанса всякий раз, когда требуется доступ к текущему аутентифицированному пользователю. Это фактически означает, что источники аутентификации кэшируются для каждого сеанса, поэтому, если вы измените AUTHENTICATION_BACKENDS, вам нужно будет очистить данные сеанса, если вам нужно заставить пользователей повторно аутентифицироваться с использованием других методов. Самый простой способ сделать это - выполнить Session.objects.all().delete().

Написание бэкенда аутентификации

Бэкэнд аутентификации - это класс, реализующий два обязательных метода: get_user(user_id)и , а также набор дополнительных методов авторизации, связанных с разрешениями .authenticate(request, **credentials)

get_userМетод берет user_id- что может быть имя пользователя, идентификатор базы данных или любой другой , но должен быть первичным ключом вашего пользовательского объекта - и возвращает объект пользователя или None.

authenticateМетод принимает requestаргумент и полномочия в качестве ключевых слов аргументов. В большинстве случаев это будет выглядеть так:

from django.contrib.auth.backends import BaseBackend

class MyBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        # Check the username/password and return a user.
        ...

Но он также может аутентифицировать токен, например:

from django.contrib.auth.backends import BaseBackend

class MyBackend(BaseBackend):
    def authenticate(self, request, token=None):
        # Check the token and return a user.
        ...

В любом случае authenticate()следует проверить полученные учетные данные и вернуть объект пользователя, который соответствует этим учетным данным, если учетные данные действительны. Если они недействительны, он должен вернуться None.

requestявляется HttpRequestи может быть, Noneесли он не был предоставлен authenticate()(который передает его в серверную часть).

Администратор Django тесно связан с объектом пользователя Django . Лучший способ справиться с этим - создать User объект Django для каждого пользователя, который существует для вашей серверной части (например, в вашем каталоге LDAP, вашей внешней базе данных SQL и т. Д.). Вы можете либо написать сценарий, чтобы сделать это заранее, либо ваш authenticateметод может сделать это при первом входе пользователя в систему.

Вот пример бэкэнда, который аутентифицируется по переменной имени пользователя и пароля, определенной в вашем settings.pyфайле, и создает User объект Django при первой аутентификации пользователя:

from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class SettingsBackend(BaseBackend):
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
    """

    def authenticate(self, request, username=None, password=None):
        login_valid = (settings.ADMIN_LOGIN == username)
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. There's no need to set a password
                # because only the password from settings.py is checked.
                user = User(username=username)
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Обработка авторизации в кастомных бэкэндах

Пользовательские серверные части аутентификации могут предоставлять свои собственные разрешения.

Модель пользователя и его менеджер будет делегировать разрешение функции поиска ( get_user_permissions(), get_group_permissions(), get_all_permissions(), has_perm(), has_module_perms(), и with_perm()) в любой серверной аутентификации , который реализует эти функции.

Разрешения, предоставленные пользователю, будут надмножеством всех разрешений, возвращаемых всеми бэкэндами. То есть Django предоставляет пользователю разрешение, которое предоставляет любой бэкэнд.

Если бэкэнд вызывает PermissionDenied исключение в has_perm()или has_module_perms(), авторизация немедленно завершится ошибкой, и Django не будет проверять последующие бэкэнды.

Бэкэнд может реализовать разрешения для волшебного администратора следующим образом:

from django.contrib.auth.backends import BaseBackend

class MagicAdminBackend(BaseBackend):
    def has_perm(self, user_obj, perm, obj=None):
        return user_obj.username == settings.ADMIN_LOGIN

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

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

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

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

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

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

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

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

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

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

Обработка прав доступа к объектам

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

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

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

В этом примере Taskмодели создаются два настраиваемых разрешения, т. Е. Действия, которые пользователи могут или не могут выполнять с Taskэкземплярами, специфичными для вашего приложения:

class Task(models.Model):
    ...
    class Meta:
        permissions = [
            ("change_task_status", "Can change the status of tasks"),
            ("close_task", "Can remove a task by setting its status as closed"),
        ]

Единственное, что это делает, - это создание этих дополнительных разрешений при запуске (функция, создающая разрешения, подключена к сигналу). Ваш код отвечает за проверку значения этих разрешений, когда пользователь пытается получить доступ к функциям, предоставляемым приложением (изменение статуса задач или закрытие задач). Продолжая приведенный выше пример, следующая проверка может ли пользователь закрывать задачи. :manage.py migratepost_migrate

user.has_perm('app.close_task')

Расширение существующей Userмодели

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

Если вы хотите сохранить информацию, относящуюся к User, вы можете использовать OneToOneFieldдля модели, содержащей поля для дополнительной информации. Эту модель «один к одному» часто называют моделью профиля, поскольку она может хранить информацию о пользователе сайта, не связанную с авторизацией. Например, вы можете создать модель Сотрудника:

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)

Предполагая, что у существующего сотрудника Фреда Смита есть и модель пользователя, и модель сотрудника, вы можете получить доступ к соответствующей информации, используя стандартные соглашения о связанных моделях Django:

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

Чтобы добавить поля модели профиля на страницу пользователя в админке, определите InlineModelAdmin(для этого примера мы будем использовать a 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модели

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

Django позволяет вам переопределить модель пользователя по умолчанию, указав значение для AUTH_USER_MODELпараметра, который ссылается на пользовательскую модель:

AUTH_USER_MODEL = 'myapp.MyUser'

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

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

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

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

Не забудьте указать AUTH_USER_MODELна это. Сделайте это перед созданием каких-либо миграций или запуском в первый раз.manage.py migrate

Также зарегистрируйте модель в приложении admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

Переход к пользовательской модели в середине проекта

Изменить AUTH_USER_MODELпосле того, как вы создали таблицы базы данных, значительно сложнее, поскольку это влияет, например, на внешние ключи и отношения «многие ко многим».

Это изменение не может быть выполнено автоматически и требует ручного исправления вашей схемы, перемещения данных из старой пользовательской таблицы и, возможно, ручного повторного применения некоторых миграций. См. # 25313 для схемы шагов.

Из-за ограничений функции динамической зависимости Django для заменяемых моделей модель, на которую ссылается, AUTH_USER_MODELдолжна быть создана при первой миграции своего приложения (обычно это называется 0001_initial); в противном случае у вас возникнут проблемы с зависимостями.

Кроме того, вы можете столкнуться с ошибкой CircularDependencyErrorпри выполнении миграции, поскольку Django не сможет автоматически разорвать цикл зависимостей из-за динамической зависимости. Если вы видите эту ошибку, вам следует разорвать цикл, переместив модели, зависящие от вашей пользовательской модели, во вторую миграцию. (Вы можете попробовать создать две нормальные модели, которые связаны ForeignKeyдруг с другом, и посмотреть, как makemigrationsразрешается эта круговая зависимость, если вы хотите увидеть, как это обычно делается.)

Многоразовые приложения и AUTH_USER_MODEL

Многоразовые приложения не должны реализовывать настраиваемую модель пользователя. В проекте может использоваться много приложений, и два многоразовых приложения, реализующих настраиваемую модель пользователя, нельзя использовать вместе. Если вам нужно хранить в информации о пользователях в вашем приложении, используйте ForeignKeyили OneToOneFieldчтобы , settings.AUTH_USER_MODEL как описано ниже.

Ссылка на Userмодель

Если вы ссылаетесь Userнапрямую (например, ссылаясь на него во внешнем ключе), ваш код не будет работать в проектах, где AUTH_USER_MODELпараметр был изменен на другую модель пользователя.

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предоставляет базовую реализацию модели пользователя, включая хешированные пароли и сброс паролей с помощью токенизации. Затем вы должны предоставить некоторые ключевые детали реализации:

класс models.CustomUser
USERNAME_FIELD

Строка, описывающая имя поля в модели пользователя, которое используется в качестве уникального идентификатора. Обычно это какое-то имя пользователя, но также может быть адрес электронной почты или любой другой уникальный идентификатор. Поле должно быть уникальным (т. Е. unique=TrueЗадано в его определении), если только вы не используете настраиваемый сервер аутентификации, который может поддерживать неуникальные имена пользователей.

В следующем примере поле identifierиспользуется как поле идентификации:

class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = 'identifier'
EMAIL_FIELD

Строка, описывающая имя поля электронной почты в Userмодели. Это значение возвращается get_email_field_name().

REQUIRED_FIELDS

Список имен полей, которые будут запрошены при создании пользователя с помощью команды createsuperuserуправления. Пользователю будет предложено ввести значение для каждого из этих полей. Она должна включать в себя любое поле , для которого blankявляется Falseили неопределенным и может включать в себя дополнительные поля , которые вы хотите запрашиваться при создании пользователя в интерактивном режиме . REQUIRED_FIELDSне влияет на другие части Django, например на создание пользователя в админке.

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

class MyUser(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

Примечание

REQUIRED_FIELDSдолжен содержать все обязательные поля в вашей пользовательской модели, но не должен содержать USERNAME_FIELDили, так passwordкак эти поля всегда будут запрашиваться.

is_active

Логический атрибут, указывающий, считается ли пользователь «активным». Этот атрибут предоставляется как атрибут по AbstractBaseUserумолчанию True. То, как вы решите реализовать это, будет зависеть от деталей выбранных вами бэкэндов аутентификации. Подробности см. В документации .is_active attribute on the built-in user model

get_full_name()

По желанию. Более длинный формальный идентификатор пользователя, например полное имя. Если реализовано, это отображается рядом с именем пользователя в истории объекта в django.contrib.admin.

get_short_name()

По желанию. Короткий неформальный идентификатор пользователя, например его имя. Если реализовано, это заменяет имя пользователя в приветствии пользователя в заголовке django.contrib.admin.

Импорт AbstractBaseUser

AbstractBaseUserи BaseUserManagerимпортируются из файлов, django.contrib.auth.base_userпоэтому их можно импортировать без включения django.contrib.authв INSTALLED_APPS.

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

класс models.AbstractBaseUser
get_username()

Возвращает значение поля, назначенного USERNAME_FIELD.

clean()

Нормализует имя пользователя путем звонка normalize_username(). Если вы переопределите этот метод, обязательно вызовите, super()чтобы сохранить нормализацию.

classmethodget_email_field_name ()

Возвращает имя поля электронной почты, указанное EMAIL_FIELDатрибутом. По умолчанию, 'email'если EMAIL_FIELDне указано.

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

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

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

AbstractUserподклассы AbstractBaseUser:

класс models.AbstractUser
clean()

Нормализует электронную почту, позвонив BaseUserManager.normalize_email(). Если вы переопределите этот метод, обязательно вызовите, super()чтобы сохранить нормализацию.

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

Вы также должны определить настраиваемый менеджер для вашей пользовательской модели. Если ваша модель пользователя определяет username, email, is_staff, is_active, is_superuser, last_login, и date_joinedполя так же , как пользователь Джанго по умолчанию, вы можете установить Джанго UserManager; однако, если ваша пользовательская модель определяет разные поля, вам необходимо определить настраиваемый менеджер, который расширяется и BaseUserManager предоставляет два дополнительных метода:

класс 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
    ...

Для ForeignKeyin USERNAME_FIELDили REQUIRED_FIELDSэти методы получают значение to_field( primary_key по умолчанию) существующего экземпляра.

BaseUserManager предоставляет следующие служебные методы:

класс models.BaseUserManager
classmethodnormalize_email ( электронная почта )

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

get_by_natural_key( имя пользователя )

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

make_random_password( длина = 10 , allowed_chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789' )

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

  • i, l, I, И 1(строчные буквы я, строчная буква L, заглавная буква я, и номер один)
  • o, OИ 0(строчная буква О, заглавная буква О, и ноль)

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

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

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

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

Следующие формы совместимы с любым подклассом 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

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

класс 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.contrib.auth.admin.UserAdmin класс Django . Однако, если ваша модель пользователя расширяется 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 предоставляет следующие методы и атрибуты:

класс models.PermissionsMixin
is_superuser

Булево. Обозначает, что у этого пользователя есть все разрешения, без их явного назначения.

get_user_permissions( obj = Нет )

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

Если objпередается, возвращает только разрешения пользователя для этого конкретного объекта.

get_group_permissions( obj = Нет )

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

Если objпередается, возвращает только разрешения группы для этого конкретного объекта.

get_all_permissions( obj = Нет )

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

Если objпередается, возвращает только разрешения для этого конкретного объекта.

has_perm( допустимо , obj = Нет )

Возвращает, Trueесли у пользователя есть указанное разрешение, где указано permв формате (см. Разрешения ). Если и оба , этот метод всегда возвращает ."<app label>.<permission codename>"User.is_activeis_superuserTrueTrue

Если objпередается, этот метод проверяет не разрешение для модели, а для этого конкретного объекта.

has_perms( perm_list , obj = Нет )

Возвращает, Trueесли у пользователя есть все указанные разрешения, где каждое разрешение находится в формате . Если и оба , этот метод всегда возвращает ."<app label>.<permission codename>"User.is_activeis_superuserTrueTrue

Если objпередается, этот метод проверяет разрешения не для модели, а для конкретного объекта.

has_module_perms( имя_пакета )

Возвращает, Trueесли у пользователя есть какие-либо разрешения в данном пакете (метка приложения Django). Если User.is_activeи is_superuserоба True, этот метод всегда возвращает True.

PermissionsMixin а также ModelBackend

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

Пользовательские пользователи и модели прокси

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

Если ваш проект использует прокси-модели, вы должны либо изменить прокси, чтобы расширить пользовательскую модель, которая используется в вашем проекте, либо объединить поведение вашего прокси-сервера с вашим Userподклассом.

Полный пример

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

Весь этот код будет находиться в models.pyфайле для пользовательского приложения аутентификации:

from django.db import models
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser
)


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=self.normalize_email(email),
            date_of_birth=date_of_birth,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, date_of_birth, password=None):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        user = self.create_user(
            email,
            password=password,
            date_of_birth=date_of_birth,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
    )
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin

Затем, чтобы зарегистрировать эту пользовательскую модель у администратора Django, в admin.pyфайле приложения потребуется следующий код :

from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError

from customauth.models import MyUser


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

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

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    disabled password hash display field.
    """
    password = ReadOnlyPasswordHashField()

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


class UserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('email', 'date_of_birth', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('date_of_birth',)}),
        ('Permissions', {'fields': ('is_admin',)}),
    )
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'date_of_birth', 'password1', 'password2'),
        }),
    )
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()


# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

Наконец, укажите пользовательскую модель в качестве модели пользователя по умолчанию для вашего проекта, используя AUTH_USER_MODELнастройку в вашем settings.py:

AUTH_USER_MODEL = 'customauth.MyUser'
Изменено в Django 3.2:

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

Copyright ©2021 All rights reserved