Spec-Zone.ru › Django 2.0

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

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

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

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

class MyBackend:
    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.hashers import check_password
from django.contrib.auth.models import User

class SettingsBackend:
    """
    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
Изменено в Django 1.11:

Параметр request был добавлен в authenticate() и поддержка модулей, которые его не принимают, будет удалена в Django 2.1.

Обработка авторизации в пользовательских модулях

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

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

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

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

Простой модуль выше может реализовать разрешения для волшебной административной панели довольно просто:

class SettingsBackend:
    ...
    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 в большинстве случаев. Если вы хотите предоставить пользовательское поведение только для части API модуля, вы можете воспользоваться наследованием Python и подклассом ModelBackend вместо реализации всего API в пользовательском модуле.

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

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

В рамках системы разрешений 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 = (
            ("view_task", "Can see available tasks"),
            ("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.view_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

В некоторых проектах требования к аутентификации могут быть такими, что встроенная модель 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 разрешит эту циклическую зависимость, если хотите увидеть, как это обычно делается.)

Приложения, используемые повторно, и модель пользователя

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

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

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

get_user_model() [source]

Вместо прямой ссылки на 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 1.11:

Добавлена возможность вызывать 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'

USERNAME_FIELD теперь поддерживает ForeignKey. Поскольку нет способа передать экземпляры моделей во время запроса createsuperuser, ожидайте, что пользователь введёт значение to_field (по умолчанию primary_key) существующего экземпляра.

EMAIL_FIELD
Новое в Django 1.11.

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

REQUIRED_FIELDS теперь поддерживает ForeignKey. Поскольку нет способа передать экземпляры моделей во время запроса createsuperuser, ожидайте, что пользователь введёт значение to_field (по умолчанию primary_key) существующего экземпляра.

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.

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

В более старых версиях подклассы должны реализовать get_short_name() и get_full_name() как AbstractBaseUser имеют реализации, которые поднимают NotImplementedError.

Импортирование 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()
Новое в Django 1.11.

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

classmethod normalize_username(username)

Применяет нормализацию Unicode NFKC к именам пользователей, чтобы визуально идентичные символы с различными кодами Unicode считались идентичными.

is_authenticated

Только для чтения атрибут, который всегда True (в отличие от AnonymousUser.is_authenticated, который всегда False). Это способ определить, был ли пользователь аутентифицирован. Это не подразумевает никаких прав и не проверяет, активен ли пользователь или имеет ли он действительную сессию. Несмотря на то, что обычно вы проверяете этот атрибут на request.user для выяснения, был ли он заполнен AuthenticationMiddleware (представляющим текущего вошедшего пользователя), вы должны знать, что этот атрибут True для любой экземпляра User.

is_anonymous

Атрибут только для чтения, который всегда False. Это способ различения объектов User и AnonymousUser. Как правило, вы должны предпочесть использование is_authenticated этому атрибуту.

set_password(raw_password)

Устанавливает пароль пользователя заданной строкой, обрабатывая хэширование пароля. Не сохраняет объект AbstractBaseUser.

Если raw_password равно None, пароль будет установлен на недопустимое значение, как если бы использовалась set_unusable_password().

check_password(raw_password)

Возвращает True если заданная строка — это правильный пароль для пользователя. (Это обрабатывает хэширование пароля для сравнения).

set_unusable_password()

Помечает пользователя как не имеющего установленного пароля. Это не то же самое, что иметь пустую строку в качестве пароля. check_password() для этого пользователя никогда не вернёт True. Не сохраняет объект AbstractBaseUser.

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

has_usable_password()

Возвращает False если set_unusable_password() был вызван для этого пользователя.

get_session_auth_hash()

Возвращает HMAC поля пароля. Используется для Отмены сеанса при изменении пароля.

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

class models.AbstractUser
clean()
Новое в Django 1.11.

Нормализует адрес электронной почты, вызвав 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, **other_fields)

Прототип create_superuser() должен принимать поле имени пользователя, а также все необходимые поля в качестве аргументов. Например, если ваша модель пользователя использует email в качестве поля имени пользователя и имеет date_of_birth как обязательное поле, то create_superuser должно быть определено как:

def create_superuser(self, email, date_of_birth, password):
    # create superuser here
    ...

В отличие от create_user(), create_superuser() обязательно требует от вызывающей стороны предоставить пароль.

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

class models.BaseUserManager
classmethod normalize_email(email)

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

get_by_natural_key(username)

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

make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')

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

  • i, l, I, и 1 (маленькая буква i, маленькая буква L, большая буква i, и цифра один)
  • o, O, и 0 (маленькая буква o, большая буква o, и ноль)

Расширение стандартной модели пользователя Django

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

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

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

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

  • AuthenticationForm: Использует поле имени пользователя, указанное в USERNAME_FIELD.
  • SetPasswordForm
  • PasswordChangeForm
  • AdminPasswordChangeForm

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

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

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

  • UserCreationForm
  • UserChangeForm

Если ваша пользовательская модель является простым подклассом AbstractUser, то вы можете расширить эти формы таким образом:

from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser

class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ('custom_field',)

Пользователи и django.contrib.admin

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

class models.CustomUser
is_staff

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

is_active

Возвращает True, если учетная запись пользователя в настоящее время активна.

has_perm(perm, obj=None):

Возвращает True, если пользователь имеет указанное разрешение. Если obj предоставлено, разрешение необходимо проверить по отношению к конкретному экземпляру объекта.

has_module_perms(app_label):

Возвращает True, если пользователю разрешен доступ к моделям в данном приложении.

Вам также необходимо зарегистрировать вашу пользовательскую модель в админ-панели. Если ваша пользовательская модель расширяет django.contrib.auth.models.AbstractUser, вы можете использовать существующий класс Django django.contrib.auth.admin.UserAdmin. Однако, если ваша модель пользователя расширяет AbstractBaseUser, вам нужно определить пользовательский класс ModelAdmin. Возможно, можно создать подкласс стандартного django.contrib.auth.admin.UserAdmin, однако вам нужно будет переопределить любые определения, которые ссылаются на поля в django.contrib.auth.models.AbstractUser, которые отсутствуют в вашем пользовательском классе.

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

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

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

class models.PermissionsMixin
is_superuser

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

get_group_permissions(obj=None)

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

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

get_all_permissions(obj=None)

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

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

has_perm(perm, obj=None)

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

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

has_perms(perm_list, obj=None)

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

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

has_module_perms(package_name)

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

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):
        """
        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 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 forms.ValidationError("Passwords don't match")
        return password2

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


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

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

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


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

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

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

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

AUTH_USER_MODEL = 'customauth.MyUser'

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/topics/auth/customizing/

Spec-Zone.ru

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