Django REST Framework: Настройка аутентификации и входа пользователей с использованием электронной почты и JWT

В современном мире веб-разработки аутентификация пользователей играет критически важную роль. Django REST Framework (DRF) предоставляет мощные инструменты для создания RESTful API, а интеграция аутентификации на основе электронной почты и JWT (JSON Web Tokens) обеспечивает безопасный и масштабируемый способ управления доступом к ресурсам. В этой статье мы рассмотрим, как настроить аутентификацию и вход пользователей в DRF с использованием электронной почты в качестве логина и JWT для защиты API.

Настройка окружения и необходимые зависимости

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

Установка Django REST Framework и необходимых пакетов

Начнем с установки Django, DRF и библиотеки для работы с JWT:

pip install django djangorestframework djangorestframework-simplejwt
  • django — основной фреймворк.

  • djangorestframework — для создания REST API.

  • djangorestframework-simplejwt — для работы с JWT.

Настройка проекта Django: создание приложения ‘users’ и моделей

Создадим новое приложение Django под названием users:

python manage.py startapp users

Добавьте rest_framework и users в INSTALLED_APPS в файле settings.py:

INSTALLED_APPS = [
    ...
    'rest_framework',
    'users',
]

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

Создание кастомной модели User с использованием email в качестве username

В файле users/models.py создадим кастомную модель пользователя, где email будет использоваться в качестве username:

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models

class UserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError('Email must be provided')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', 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, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, verbose_name='email address')
    first_name = models.CharField(max_length=30, blank=True)
    last_name = models.CharField(max_length=30, blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __str__(self):
        return self.email

Укажите эту модель в AUTH_USER_MODEL в settings.py:

AUTH_USER_MODEL = 'users.User'

Выполните миграции:

python manage.py makemigrations
python manage.py migrate

Создание сериализаторов для регистрации и входа пользователей

Создадим сериализаторы в users/serializers.py для регистрации и входа пользователей:

from rest_framework import serializers
from .models import User
from django.contrib.auth import authenticate

class RegistrationSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
    password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'password', 'password2')
        extra_kwargs = {
            'first_name': {'required': True},
            'last_name': {'required': True}
        }

    def validate(self, data):
        if data['password'] != data['password2']:
            raise serializers.ValidationError("Passwords do not match.")
        return data

    def create(self, validated_data):
        validated_data.pop('password2')
        password = validated_data.pop('password')
        user = User.objects.create_user(email=validated_data['email'],
                                        first_name=validated_data['first_name'],
                                        last_name=validated_data['last_name'])
        user.set_password(password)
        user.save()
        return user


class LoginSerializer(serializers.Serializer):
    email = serializers.EmailField(required=True)
    password = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})

    def validate(self, data):
        email = data.get('email', None)
        password = data.get('password', None)

        if email and password:
            user = authenticate(email=email, password=password)
            if user:
                if user.is_active:
                    data['user'] = user
                else:
                    raise serializers.ValidationError("User is not active.")
            else:
                raise serializers.ValidationError("Unable to login with provided credentials.")
        else:
            raise serializers.ValidationError("Must include 'email' and 'password'.")
        return data
Реклама

Реализация логики аутентификации и работы с JWT

Создание представлений (views) для регистрации и входа

Создадим представления для регистрации и входа пользователей в users/views.py:

from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import RegistrationSerializer, LoginSerializer

class RegistrationAPIView(generics.GenericAPIView):
    serializer_class = RegistrationSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class LoginAPIView(generics.GenericAPIView):
    serializer_class = LoginSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid(raise_exception=True):
            user = serializer.validated_data['user']
            refresh = RefreshToken.for_user(user)
            return Response({
                'refresh': str(refresh),
                'access': str(refresh.access_token),
            }, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Настройка аутентификации на основе JWT: генерация токенов и защита API

Добавим URL-пути в users/urls.py:

from django.urls import path
from .views import RegistrationAPIView, LoginAPIView

urlpatterns = [
    path('register/', RegistrationAPIView.as_view(), name='register'),
    path('login/', LoginAPIView.as_view(), name='login'),
]

Включим эти URL-пути в основной urls.py файл проекта:

from django.urls import path, include

urlpatterns = [
    path('api/users/', include('users.urls')),
]

Настроим DRF для использования JWT аутентификации в settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}

Безопасность, обработка ошибок и дополнительные улучшения

Рекомендации по безопасности: хэширование паролей, валидация данных

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

  • Валидация данных: Валидируйте все входящие данные на сервере, чтобы предотвратить SQL-инъекции и другие уязвимости. DRF предоставляет мощные инструменты для валидации данных в сериализаторах.

  • HTTPS: Используйте HTTPS для шифрования трафика между клиентом и сервером, защищая данные аутентификации от перехвата.

  • CORS: Настройте CORS (Cross-Origin Resource Sharing) для контроля доступа к API только с доверенных доменов.

Обработка ошибок и предоставление понятных сообщений пользователям

  • Обработка исключений: Перехватывайте исключения и возвращайте понятные сообщения об ошибках пользователям. Используйте middleware для глобальной обработки исключений.

  • Валидация на стороне клиента: Добавьте валидацию на стороне клиента для улучшения пользовательского опыта и снижения нагрузки на сервер.

  • Логирование: Включите логирование для отслеживания ошибок и подозрительной активности.

Заключение

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


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