Современный способ создание настраиваемой модели User в Django – Еще один блог веб-разработчика

Custom views for account management

We will next look at custom account-related operations, such as: logging in, signing up and viewing the account information. This section shows you how to do it with custom views. In most cases it would be better to use a library for these common operations, so feel free to skip ahead to the next section, where we will cover the same using the Allauth library.

Auth templates

These templates will be used by the auth app views.Auth templates will exend a base template.

Login template used by LoginView.

Register template used by RegistrationView.

Change password template used by PasswordChangeView.

Password change done template used by PasswordChangeDoneView.

Password Reset template used by PasswordResetView.

Password reset email template used by PasswordResetView.

Password reset done template used by PasswordResetDoneView.

Password reset confirm template used by PasswordResetConfirmView.

Password reset complete template used by PasswordResetCompleteView.

Edit Profile template used by ProfileView.

Configuration

Assuming you start with a default settings.py, these are the changes required by Allauth in settings.py:

Core templates

These templates will be used by the core app and should be saved in the project level templates folder.

The Base Template

The Home template

Listing detail template

Add Listing template

Getting started

On Ubuntu 18.04 you might need to add the universe repository and install python3-venv.

Prerequisites

You will need to have working knowledge of Python and the Django Framework.The Django version used in this tutorial is 3.04.A recent version of Python 3. The instructions in this tutorial were performed on Ubuntu 18.04 LTS with Python 3.6.

Project setup

Create and activate the virtual environment.

The accounts app

Let’s add an app to host our authentiation customizations.

The core app

Now let’s implement the core app functionality which includes:

We will use Bootstrap 4 and django-crispy-forms for styling the application interface and forms.

The model and manager

# accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.contrib.auth import get_user_model
from django.utils import timezone


class AccountManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, name, phone, password, **extra_fields):
        values = [email, name, phone]
        field_value_map = dict(zip(self.model.REQUIRED_FIELDS, values))
        for field_name, value in field_value_map.items():
            if not value:
                raise ValueError('The {} value must be set'.format(field_name))

        email = self.normalize_email(email)
        user = self.model(
            email=email,
            name=name,
            phone=phone,
            **extra_fields
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, name, phone, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, name, phone, password, **extra_fields)

    def create_superuser(self, email, name, phone, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, name, phone, password, **extra_fields)


class Account(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=150)
    phone = models.CharField(max_length=50)
    date_of_birth = models.DateField(blank=True, null=True)
    picture = models.ImageField(blank=True, null=True)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)
    last_login = models.DateTimeField(null=True)

    objects = AccountManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['name', 'phone']

    def get_full_name(self):
        return self.name

    def get_short_name(self):
        return self.name.split()[0]

The urls

Let’s start with th project-level URLs. We will re-use the auth app views for Login, Logout, Password Change & Password Reset.

# listings/urls.py
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views

from accounts import views
from accounts.views import RegistrationView, ProfileView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('core.urls')),

    path('register/', RegistrationView.as_view(), name='register'),
    path('profile/', ProfileView.as_view(), name='profile'),
    path('accounts/login/', auth_views.LoginView.as_view(), name='login'),
    path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),

    path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
    path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),

    path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
    path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
    path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
    path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]

Then the core app URLs

# core/urls.py
from django.urls import path, include

from .views import HomeView, CreateListing, ListingView

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
    path('add-listing', CreateListing.as_view(), name='add_listing'),
    path('listings/<slug:slug>/', ListingView.as_view(), name='listing'),
]

The auth views render templates from a registration folder. We will create a project level templates folder for all our templates and a registration subfolder.

Then add the templates folder to the DIRS setting in settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Try it out

Allauth gives a range of account functionalities. Try them out:

Clone the repository to view the code at this point.

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

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

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

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

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

Обработка авторизации в пользовательских бэкендах

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

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

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

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

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

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

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

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

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

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

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

Создание нашей начальной пользовательской модели пользователя требует четырех шагов:

Обработка авторизации в пользовательских бэкендах¶

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

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

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

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

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

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

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

Единственное, что это делает – создает эти дополнительные разрешения, когда вы выполняете manage.pymigrate (функция, создающая разрешения, подключена к сигналу post_migrate).

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

Суперпользователь

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

Установка

Для начала создайте новый проект Django из командной строки. Нам нужно сделать несколько вещей:

Здесь приведены команды для выполнения:

Conclusion

This article has shown how to customize Django authentication to implement the following features:

The Source code for this article can be found here.

Похожее:  Аутентификация, авторизация и учет (AAA) – RADIUS или TACACS

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *