Что такое 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 включают:
- Простота: Легко генерировать и проверять.
- Безопасность: Подпись гарантирует целостность и подлинность токена.
- Масштабируемость: Не требует хранения сессий на сервере.
Преимущества использования 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 токена
- Зарегистрируйте пользователя через
/api/register/. - Войдите в систему через
/api/login/, чтобы получитьaccessиrefreshтокены. - Используйте
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 у пользователя, прежде чем разрешить доступ к ресурсу.