Spec-Zone.ru › Django 2.1

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

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

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

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

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

Модель пользователя делегирует функции поиска разрешений (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 атрибут мета модели.

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

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

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

user.has_perm('app.close_task')

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

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

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

from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)

Предполагая, что существует существующий сотрудник Fred Smith, у которого есть как модель 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'

Эта пара с точкой описывает имя приложения 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() [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 ожидает, что ваша пользовательская модель будет соответствовать некоторым минимальным требованиям.

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

Самый простой способ создать соответствующую пользовательскую модель — это унаследовать от AbstractBaseUser. AbstractBaseUser предоставляет основную реализацию модели пользователя, включая хэшированные пароли и токенизированные сбросы паролей. Вам необходимо предоставить некоторые ключевые детали реализации:

class models.CustomUser
USERNAME_FIELD

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

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

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

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

REQUIRED_FIELDS

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

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

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

Примечание

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

is_active

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

get_full_name()

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

get_short_name()

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

Изменено в 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()

Возвращает имя поля электронной почты, указанное атрибутом 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()

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

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

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

class models.CustomUserManager
create_user(*username_field*, password=None, **other_fields)

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

def create_user(self, email, date_of_birth, password=None):
    # create user here
    ...
create_superuser(*username_field*, password, **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() обязательно требует, чтобы вызывающий метод передавал пароль.

Для 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 (маленькая буква 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>" (см. разрешения). Если User.is_active и is_superuser оба True, этот метод всегда возвращает True.

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

has_perms(perm_list, obj=None)

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

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

has_module_perms(package_name)

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

PermissionsMixin и ModelBackend

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

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

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

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

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

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

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

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


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

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

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

    def create_superuser(self, email, date_of_birth, password):
        """
        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.1/topics/auth/customizing/

Spec-Zone.ru

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