Виммельбух, 3, перетяжка
Виммельбух, 3, перетяжка
Виммельбух, 3, перетяжка

Как настроить аутентификацию в веб-приложениях на Django

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

607 открытий4К показов
Как настроить аутентификацию в веб-приложениях на Django

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

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

Базовая аутентификация в Django

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

Установим фреймворк командой pip:

			pip install django
		

Создадим новый проект:

			django-admin startproject myproject
cd myproject
		

Создадим новое приложение для работы с аутентификацией:

			python manage.py startapp myapp
		
Django уже содержит встроенную систему аутентификации. Она находится в приложении django.contrib.auth, которое по умолчанию включено в INSTALLED_APPS в файле settings.py.

Добавим приложение в список INSTALLED_APPS:

			INSTALLED_APPS = [
    ...
    'myapp',
]
		

Добавление и настройка страниц для входа, регистрации и выхода

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

			from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class LoginForm(forms.Form):
    username = forms.CharField(label='Username')
    password = forms.CharField(label='Password', widget=forms.PasswordInput)

class RegisterForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']


		

Код выше создает две формы: LoginForm для входа и RegisterForm для регистрации нового пользователя.

Форма LoginForm содержит два поля: username и password. Поле password отображается как поле ввода пароля с помощью виджета PasswordInput.

Форма RegisterForm наследуется от встроенной формы UserCreationForm и добавляет дополнительное поле email. В классе Meta указываем модель User и поля, которые нужно включить в форму.

Создадим представления для обработки запросов входа, регистрации и выхода. В файле views.py приложения myapp добавим код:

			from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from .forms import LoginForm, RegisterForm

def login_view(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(request, username=username, password=password)
            if user is not None:
                login(request, user)
                return redirect('home')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})

def register_view(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
    else:
        form = RegisterForm()
    return render(request, 'register.html', {'form': form})

def logout_view(request):
    logout(request)
    return redirect('login')


		

Код выше создает три функции представления: login_view, register_view и logout_view.

Функция login_view обрабатывает запрос входа пользователя. Для POST-запроса создается экземпляр формы LoginForm с переданными данными. Если форма валидна, извлекаются имя, пароль и выполняется аутентификация пользователя с помощью функции authenticate(). Если пользователь успешно аутентифицирован, выполняется вход login(), и пользователь перенаправляется на главную. Для GET-запроса, создается пустая форма LoginForm, которая передается в шаблон login.html.

Функция register_view обрабатывает запрос регистрации.

  • Если это POST-запрос, создается экземпляр формы RegisterForm с переданными данными. 
  • Если форма валидна, новый пользователь сохраняется в базе данных с помощью метода save(), и перенаправляется на страницу входа. 
  • Если это GET-запрос, создается пустая форма RegisterForm, которая передается в шаблон register.html.

Функция logout_view обрабатывает запрос завершения сессии. Она вызывает функцию logout() для выхода пользователя, и перенаправляет его на страницу входа.

Создадим шаблоны login.html, register.html и добавим ссылку на выход в шаблоне base.html:

			<!-- login.html -->
<h2>Войти</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Войти</button>
</form>
		
			<!-- register.html -->
<h2>Зарегистрироваться</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Зарегистрироваться</button>
</form>
		
			<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
    <header>
        {% if user.is_authenticated %}
            <p>Добро пожаловать, {{ user.username }}! <a href="{% url 'logout' %}">Выйти</a></p>
        {% else %}
            <p><a href="{% url 'login' %}">Войти</a> | <a href="{% url 'register' %}">Зарегистрироваться</a></p>
        {% endif %}
    </header>

    {% block content %}
    {% endblock %}
</body>
</html>
		
В шаблоне base.html добавляем условие {% if user.is_authenticated %} — оно проверит, авторизован ли пользователь. Если авторизован, отобразится приветствие и ссылка на выход. Если не авторизован, то появятся ссылки на страницы входа и регистрации.

Обновим файл urls.py приложения myapp, чтобы добавить маршруты для новых представлений:

			from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login_view, name='login'),
    path('register/', views.register_view, name='register'),
    path('logout/', views.logout_view, name='logout'),
]
		

Запустим сервер, чтобы протестировать вход и регистрацию:

			python manage.py runserver
		
Страницы размещаются по адресу http://localhost:8000/login/ и http://localhost:8000/register/ соответственно.

В Django также есть встроенные классы представлений для аутентификации, такие как LoginView и LogoutView. Чтобы использовать их, обновим файл urls.py приложения myapp:

			from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

urlpatterns = [
    path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
    path('register/', views.register_view, name='register'),
    path('logout/', auth_views.LogoutView.as_view(next_page='login'), name='logout'),
]
		

Заменили функцию представления login_view на класс представления LoginView и указали шаблон login.html. Аналогично, заменили функцию представления logout_view на класс представления LogoutView и указали страницу, на которую будет перенаправлен пользователь после выхода (next_page='login').

Управление доступом с помощью авторизации

Один из распространенных способов ограничения доступа — использование декоратора @login_required. Его можно применять к функциям представления, чтобы разрешить доступ только авторизованным пользователям.

Для использования декоратора @login_required обновим файл settings.py и укажем URL-адрес для перенаправления неавторизованных пользователей:

			LOGIN_URL = '/login/'
		

Теперь в файле views.py приложения myapp добавим следующий код:

			from django.contrib.auth.decorators import login_required

@login_required
def home_view(request):
    return render(request, 'home.html')
		

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

Создадим шаблон home.html в директории templates приложения myapp:

			<!-- home.html -->
{% extends 'base.html' %}

{% block content %}
    <h2>Добро пожаловать на домашнюю страницу!</h2>    <p>Это защищенная страница. Доступ к нему могут получить только авторизованные пользователи.</p>{% endblock %}
		

Добавим путь для главной страницы в файле urls.py приложения myapp:

			from django.urls import path
from . import views

urlpatterns = [
    path('', views.home_view, name='home'),
    # ...
]
		
Если неавторизованный пользователь попытается получить доступ к главной странице, он будет перенаправлен на страницу входа.

В Django есть система разрешений (permissions) для расширенного управления доступом. С их помощью можно определять, какие действия могут выполнять пользователи или группы пользователей.

Создадим группу и назначим ей разрешения. В файле admin.py приложения myapp добавим следующий код:

			from django.contrib import admin
from django.contrib.auth.models import Group, Permission

admin.site.register(Group)
admin.site.register(Permission)
		

Создадим функцию представления, которая будет доступна только пользователям с определенным разрешением. В файле views.py приложения myapp добавим код:

			from django.contrib.auth.decorators import permission_required

@permission_required('myapp.view_special_page')
def special_page_view(request):
    return render(request, 'special_page.html')
		
Создаем функцию представления special_page_view, которая отображает специальную страницу. Применяем декоратор @permission_required к этой функции и указываем необходимое разрешение myapp.view_special_page. Только пользователи или группы с этим разрешением смогут получить доступ к данной странице.

Создадим шаблон special_page.html в директории templates приложения myapp:

			<!-- special_page.html -->
{% extends 'base.html' %}

{% block content %}
    <h2>Добро пожаловать на специальную страницу!</h2>
    <p>Эта страница доступна только пользователям с особыми разрешениями.</p>
{% endblock %}
		

Добавим маршрут для специальной страницы в файле urls.py приложения myapp:

			from django.urls import path
from . import views

urlpatterns = [
    # ...
    path('special/', views.special_page_view, name='special_page'),
]
		

Теперь только пользователи или группы с разрешением myapp.view_special_page смогут получить доступ к специальной странице.

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

Можно создать собственную модель пользователя, наследуя от AbstractUser или AbstractBaseUser.

Давайте ассмотрим создание кастомной модели пользователя с помощью AbstractUser. В файле models.py приложения myapp добавим код:

			from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    phone_number = models.CharField(max_length=20, blank=True)
    address = models.TextField(blank=True)
    
    def __str__(self):
        return self.username
		

Создаем класс CustomUser, который наследуется от AbstractUser. Добавляем дополнительные поля phone_number и address к модели пользователя.

Обновим файл settings.py и укажем кастомную модель:

			AUTH_USER_MODEL = 'myapp.CustomUser'
		
С этого момента система аутентификации будет использовать кастомную модель пользователя вместо встроенной модели User.

Создадим миграции и применим их для обновления базы данных:

			python manage.py makemigrations
python manage.py migrate
		

Для работы с кастомной моделью нужно обновить формы. В файле forms.py приложения myapp изменим поля регистрации:

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

class CustomUserCreationForm(UserCreationForm):
    phone_number = forms.CharField(max_length=20, required=False)
    address = forms.CharField(widget=forms.Textarea, required=False)

    class Meta:
        model = CustomUser
        fields = ['username', 'email', 'phone_number', 'address', 'password1', 'password2']
		

Создаем класс CustomUserCreationForm, который наследуется от UserCreationForm. Добавляем поля phone_number и address к форме и указываем кастомную модель CustomUser в классе Meta.

Чтобы система аутентификации приняла изменения, обновим функцию представления register_view в файле views.py:

			from .forms import CustomUserCreationForm

def register_view(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
    else:
        form = CustomUserCreationForm()
    return render(request, 'register.html', {'form': form})


		

Меняем форму UserCreationForm на кастомную форму CustomUserCreationForm. Теперь Django работает, используя собственную модель пользователя с дополнительными полями во всем проекте.

Социальная аутентификация

Для реализации входа через сторонние сервисы есть библиотека django-allauth. Она предоставляет готовые решения для интеграции с провайдерами социальной аутентификации.

Установим библиотеку django-allauth:

			pip install django-allauth
		

Добавим django-allauth в список установленных приложений в файле settings.py:

			INSTALLED_APPS = [
    # ...
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    # ...
]

SITE_ID = 1
		

Добавляем приложения django.contrib.sites, allauth и провайдеры социальной аутентификации, которые хотим использовать. В нашем случае это интеграция Google.

Также устанавливаем значение SITE_ID = 1, чтобы указать идентификатор сайта.

Добавим URL-адреса для django-allauth в файле urls.py проекта:

			from django.urls import path, include

urlpatterns = [
    # ...
    path('accounts/', include('allauth.urls')),
    # ...
]
		

Включаем URL-адреса django-allauth с префиксом accounts/.

Выполним миграции для создания таблиц в базе данных:

			python manage.py migrate
		

После этого можно настраивать провайдеров социальной аутентификации через административный интерфейс Django.

Добавим кнопки для социальной аутентификации на страницу входа. В шаблоне login.html добавим следующий код:

			<!-- login.html -->
{% extends 'base.html' %}

{% block content %}
    <h2>Войти</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Войти</button>
    </form>

    <h3>Или войти через:</h3>
    <a href="{% url 'socialaccount_signup' 'google' %}">Google</a>
{% endblock %}
		

Добавляем ссылки на страницы регистрации через социальные аккаунты, используя URL-адреса socialaccount_signup с указанием провайдера. Теперь пользователи могут войти на сайт, используя свою учетную запись Google.

Безопасность аутентификации

Рекомендации по повышению безопасности аутентификации:

  • Настройте SSL/TLS-сертификат для сайта для безопасного соединения между клиентом и сервером. Используйте протокол HTTPS для всех страниц, где есть аутентификации и передача конфиденциальных данных. 
  • Система на Django работает со встроенной защитой от межсайтовой подделки запросов (CSRF). Убедитесь, что в шаблонах форм используется тег {% csrf_token %}. 
  • Экранируйте пользовательский ввод перед его выводом на страницах сайта, чтобы защититься от межсайтового скриптинга (XSS). Django автоматически экранирует переменные в шаблонах, но будьте осторожны при явном выводе HTML.
  • Для защиты от атак методом перебора (брутфорса) ограничьте количество неудачных попыток входа для каждого пользователя или IP-адреса. Используйте библиотеки django-axes или django-ratelimit, чтобы реализовать ограничения числа попыток входа.
  • Никогда не храните пароли в открытом виде — вместо этого используйте хеш-функции. В Django есть встроенные функции для хеширования паролей: django.contrib.auth.hashers.make_password() и django.contrib.auth.hashers.check_password().
  • Возможно, стоит реализовать двухфакторную аутентификацию, чтобы повысить безопасность учетных записей. Используйте библиотеки django-two-factor-auth или django-otp для добавления 2FA.
Следите за новыми постами
Следите за новыми постами по любимым темам
607 открытий4К показов