Как создать Django REST API с аутентификацией JWT?

Что такое Django REST Framework (DRF)?

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

Что такое JSON Web Token (JWT) и зачем он нужен?

JSON Web Token (JWT) — это компактный, самодостаточный способ безопасной передачи информации между сторонами в виде JSON-объекта. Он используется для аутентификации и авторизации в веб-приложениях. JWT содержит claims (утверждения) о пользователе и подписывается криптографическим алгоритмом. Ключевые преимущества JWT включают:

  1. Простота: Легко генерировать и проверять.
  2. Безопасность: Подпись гарантирует целостность и подлинность токена.
  3. Масштабируемость: Не требует хранения сессий на сервере.

Преимущества использования JWT для аутентификации в REST API

Использование JWT для аутентификации в REST API предоставляет следующие преимущества:

  • Без сохранения состояния (Stateless): Сервер не хранит информацию о сессии пользователя, что упрощает масштабирование.
  • Поддержка различных платформ: JWT может использоваться с любым языком программирования и платформой.
  • Безопасность: Токены подписываются и могут быть зашифрованы для дополнительной безопасности.
  • Гибкость: Можно добавлять пользовательские claims в токен.

Настройка Django проекта для REST API с JWT

Создание нового Django проекта и приложения

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

django-admin startproject myproject
cd myproject
python manage.py startapp myapp

Установка Django REST Framework и Simple JWT

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

pip install djangorestframework
pip install djangorestframework-simplejwt

Настройка settings.py для DRF и JWT

Добавьте rest_framework и rest_framework_simplejwt в INSTALLED_APPS в settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_simplejwt',
    'myapp',
]

Настройте DRF и Simple JWT в settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': True,

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUTH_HEADER_TYPES': ('Bearer',),
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
}

Реализация аутентификации с использованием Simple JWT

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

Создайте файл serializers.py в вашем приложении (myapp) и добавьте следующие сериализаторы:

from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
from django.core import exceptions

class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)

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

    def validate(self, attrs):
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError({"password": "Password fields didn't match."})

        return attrs

    def create(self, validated_data):
        user = User.objects.create(
            username=validated_data['username'],
            email=validated_data['email'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name']
        )

        user.set_password(validated_data['password'])
        user.save()

        return user

class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField(write_only=True)


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

        if username and password:
            user = authenticate(username=username, password=password)
            if user:
                if user.is_active:
                    data['user'] = user
                else:
                    raise exceptions.ValidationError('Аккаунт заблокирован.')
            else:
                raise exceptions.ValidationError('Неверные учетные данные.')
        else:
            raise exceptions.ValidationError('Необходимо указать имя пользователя и пароль.')

        return data




from django.contrib.auth import authenticate
Реклама

Написание представлений (views) для регистрации, входа и обновления токенов

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

from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import RegisterSerializer, LoginSerializer
from rest_framework.permissions import AllowAny


class RegisterView(generics.GenericAPIView):
    serializer_class = RegisterSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        return Response({"message": "Пользователь успешно создан"}, status=status.HTTP_201_CREATED)

class LoginView(generics.GenericAPIView):
    serializer_class = LoginSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        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),
        })

Настройка URL-маршрутов для endpoints аутентификации

Создайте файл urls.py в вашем приложении и настройте URL-маршруты:

from django.urls import path
from .views import RegisterView, LoginView

urlpatterns = [
    path('register/', RegisterView.as_view(), name='register'),
    path('login/', LoginView.as_view(), name='login'),
]

В urls.py основного проекта добавьте URL-маршруты вашего приложения:

from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls')),
]

Защита API endpoints с помощью JWT Authentication

В settings.py у вас уже настроена аутентификация по умолчанию. Если нужно защитить отдельные view-функции:

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def protected_view(request):
    return Response({'message': 'Этот endpoint защищен JWT аутентификацией!'})

Работа с защищенными ресурсами

Создание модели и сериализатора для примера ресурса

Создайте модель в models.py:

from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=255)
    owner = models.ForeignKey('auth.User', related_name='items', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

Создайте сериализатор для модели в serializers.py:

from rest_framework import serializers
from .models import Item

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ['id', 'name', 'owner']

Создание представления (view) для доступа к ресурсу только для аутентифицированных пользователей

Создайте представление в views.py:

from rest_framework import generics
from .models import Item
from .serializers import ItemSerializer
from rest_framework import permissions

class ItemList(generics.ListCreateAPIView):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
    permission_classes = [permissions.IsAuthenticated]

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)


class ItemDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
    permission_classes = [permissions.IsAuthenticated]

Тестирование API endpoints с использованием JWT токена

  1. Зарегистрируйте пользователя через /api/register/.
  2. Войдите в систему через /api/login/, чтобы получить access и refresh токены.
  3. Используйте access токен в заголовке Authorization: Bearer <access_token>, чтобы получить доступ к защищенным ресурсам.

Продвинутые настройки и кастомизация JWT

Настройка времени жизни токенов (access и refresh)

Настройте время жизни токенов в settings.py:

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

Кастомизация JWT payload (добавление дополнительной информации)

Создайте функцию для добавления дополнительной информации в payload токена:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        # Добавьте пользовательские claims
        token['username'] = user.username
        token['email'] = user.email
        # ...

        return token

class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

Реализация ролевой системы доступа (RBAC) с JWT

Добавьте поле role в модель User, создайте пользовательские permissions и views, проверяющие роль пользователя.

Пример: Допустим, у вас есть роли admin и user. При генерации JWT, добавьте информацию о роли в payload, а затем в каждом view проверяйте наличие роли admin у пользователя, прежде чем разрешить доступ к ресурсу.


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