💻 Примеры: Auth-блок в действии
⚡ Ключевые примеры
- TokenAuthentication:
Authorization: Token abc123 - JWT логин: POST /api/token/ → {access, refresh}
- IsOwnerOrReadOnly: только владелец объекта может редактировать
- @receiver(post_save, sender=User): создать токен при регистрации
- send_mail() через консольный бэкенд для тестирования
Пример 1: TokenAuthentication — полный цикл
# settings.py
INSTALLED_APPS = [
'rest_framework',
'rest_framework.authtoken',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
# urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
path('api-token-auth/', obtain_auth_token, name='api_token_auth'),
path('books/', BookListView.as_view()),
]
# views.py
from rest_framework.generics import ListAPIView
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from .models import Book
from .serializers import BookSerializer
class BookListView(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
Запрос через Postman: POST /api-token-auth/ с body (form-data): username, password → получаем {"token": "abc123..."}
GET /books/ с заголовком: Authorization: Token abc123...
GET /books/ с заголовком: Authorization: Token abc123...
Пример 2: JWT с httpOnly-куки (полный цикл)
# settings.py
from datetime import timedelta
REST_FRAMEWORK = {
'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': True,
'BLACKLIST_AFTER_ROTATION': True,
}
MIDDLEWARE = [
...
'first_app.middleware.JWTAuthenticationMiddleware',
]
# views.py — LoginView с сохранением в куки
from datetime import datetime
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth import authenticate
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.permissions import AllowAny
class LoginView(APIView):
permission_classes = [AllowAny]
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
if user:
refresh = RefreshToken.for_user(user)
access_token = refresh.access_token
response = Response(status=status.HTTP_200_OK)
response.set_cookie(
key='access_token',
value=str(access_token),
httponly=True,
secure=False, # True на HTTPS
samesite='Lax',
expires=datetime.utcfromtimestamp(access_token['exp'])
)
response.set_cookie(
key='refresh_token',
value=str(refresh),
httponly=True,
secure=False,
samesite='Lax',
expires=datetime.utcfromtimestamp(refresh['exp'])
)
return response
return Response({'detail': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)
# views.py — LogoutView
class LogoutView(APIView):
def post(self, request, *args, **kwargs):
response = Response(status=status.HTTP_204_NO_CONTENT)
response.delete_cookie('access_token')
response.delete_cookie('refresh_token')
return response
Пример 3: Регистрация пользователя с JWT
# serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, min_length=8)
class Meta:
model = User
fields = ['username', 'password', 'email']
def create(self, validated_data):
return User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
email=validated_data.get('email', '')
)
# views.py
from rest_framework_simplejwt.tokens import RefreshToken
class RegisterView(APIView):
permission_classes = [AllowAny]
def post(self, request):
serializer = RegisterSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
refresh = RefreshToken.for_user(user)
response = Response(
{'user': {'username': user.username, 'email': user.email}},
status=status.HTTP_201_CREATED
)
response.set_cookie('access_token', str(refresh.access_token), httponly=True)
response.set_cookie('refresh_token', str(refresh), httponly=True)
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Пример 4: Кастомное разрешение IsOwnerOrReadOnly
# permissions.py
from rest_framework.permissions import BasePermission
class IsOwnerOrReadOnly(BasePermission):
"""Только владелец может изменять/удалять; все могут читать."""
def has_object_permission(self, request, view, obj):
if request.method in ('GET', 'HEAD', 'OPTIONS'):
return True
return obj.owner == request.user
# Расширенный вариант: владелец ИЛИ администратор
class IsAdminOrOwner(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in ('GET', 'HEAD', 'OPTIONS'):
return True
return request.user.is_staff or obj.owner == request.user
# views.py
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .permissions import IsOwnerOrReadOnly
class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
Пример 5: Сигналы — автосоздание токена и email-уведомление
# signals.py
import logging
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.mail import send_mail
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User
from .models import Book
logger = logging.getLogger(__name__)
# Автосоздание токена при регистрации пользователя
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
# Уведомление администратора при создании книги
@receiver(post_save, sender=Book)
def notify_admin_on_book_create(sender, instance, created, **kwargs):
if created:
send_mail(
subject='New Book Created',
message=f'Book "{instance.title}" (id={instance.id}) has been created.',
from_email='admin@example.com',
recipient_list=['admin@example.com'],
)
# Логирование удаления книги
@receiver(post_delete, sender=Book)
def log_book_deletion(sender, instance, **kwargs):
logger.info(f'Book deleted: id={instance.id}, title={instance.title}')
# apps.py — обязательно для регистрации сигналов
from django.apps import AppConfig
class FirstAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'first_app'
def ready(self):
import first_app.signals # регистрируем обработчики
Пример 6: Кастомные разрешения модели
# models.py
class Genre(models.Model):
name = models.CharField(max_length=100, unique=True)
class Meta:
permissions = [
('can_get_statistic', 'Can get genres statistic'),
]
# permissions.py
class CanGetStatisticPermission(BasePermission):
def has_permission(self, request, view):
return request.user.has_perm('first_app.can_get_statistic')
# views.py
from rest_framework.permissions import DjangoModelPermissions
from .permissions import CanGetStatisticPermission
class GenreViewSet(ModelViewSet):
queryset = Genre.objects.all()
serializer_class = GenreSerializer
permission_classes = [DjangoModelPermissions, CanGetStatisticPermission]
Важно: После добавления Meta.permissions обязательно выполнить
python manage.py makemigrations && python manage.py migrate, иначе разрешение не появится в Django Admin.
Пример 7: Полная настройка Swagger
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
'drf_yasg',
]
# urls.py (проект)
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title='My API',
default_version='v1',
description='API документация',
contact=openapi.Contact(email='contact@example.com'),
license=openapi.License(name='MIT'),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('first_app.urls')),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]