Токенная аутентификация DRF

Токены обеспечивают безопасный доступ к API без передачи логина и пароля. Это стандартный способ аутентификации для мобильных приложений и SPA.

Настройка токенов в settings.py

 1INSTALLED_APPS = [
 2    'rest_framework',
 3    'rest_framework.authtoken',
 4]
 5
 6REST_FRAMEWORK = {
 7    'DEFAULT_AUTHENTICATION_CLASSES': [
 8        'rest_framework.authentication.TokenAuthentication',
 9    ],
10    'DEFAULT_PERMISSION_CLASSES': [
11        'rest_framework.permissions.IsAuthenticated',
12    ]
13}

Создание токенов

Есть несколько способов создать токены для пользователей:

1. Автоматическое создание при регистрации

1from django.contrib.auth.models import User
2from rest_framework.authtoken.models import Token
3
4def create_user_with_token(username, email, password):
5    user = User.objects.create_user(username=username, email=email, password=password)
6    token, created = Token.objects.get_or_create(user=user)
7    return user, token

2. Создание через Django shell

1# В Django shell
2from django.contrib.auth.models import User
3from rest_framework.authtoken.models import Token
4
5user = User.objects.get(username='admin')
6token, created = Token.objects.get_or_create(user=user)
7print(f"Token: {token.key}")

3. Создание через management command

 1# management/commands/create_tokens.py
 2from django.core.management.base import BaseCommand
 3from django.contrib.auth.models import User
 4from rest_framework.authtoken.models import Token
 5
 6class Command(BaseCommand):
 7    help = 'Create tokens for all users'
 8
 9    def handle(self, *args, **options):
10        for user in User.objects.all():
11            token, created = Token.objects.get_or_create(user=user)
12            if created:
13                self.stdout.write(f"Created token for {user.username}")
14            else:
15                self.stdout.write(f"Token already exists for {user.username}")

Защита API endpoints

Используй декораторы и миксины для защиты views:

Защита Class-Based Views

 1from rest_framework import generics
 2from rest_framework.permissions import IsAuthenticated
 3from rest_framework.authentication import TokenAuthentication
 4
 5class UserProfileView(generics.RetrieveUpdateAPIView):
 6    authentication_classes = [TokenAuthentication]
 7    permission_classes = [IsAuthenticated]
 8    serializer_class = UserSerializer
 9
10    def get_object(self):
11        return self.request.user

Защита Function-Based Views

 1from rest_framework.decorators import api_view, permission_classes, authentication_classes
 2from rest_framework.permissions import IsAuthenticated
 3from rest_framework.authentication import TokenAuthentication
 4from rest_framework.response import Response
 5
 6@api_view(['GET'])
 7@authentication_classes([TokenAuthentication])
 8@permission_classes([IsAuthenticated])
 9def protected_view(request):
10    return Response({
11        'message': f'Привет, {request.user.username}!',
12        'user_id': request.user.id
13    })

Использование токенов в запросах

Токен передается в заголовке Authorization:

Пример с curl

1curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
2    http://localhost:8000/api/profile/

Пример с Python requests

1import requests
2
3headers = {
4    'Authorization': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
5}
6
7response = requests.get('http://localhost:8000/api/profile/', headers=headers)
8print(response.json())

Пример с JavaScript fetch

 1const token = '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b';
 2
 3fetch('/api/profile/', {
 4    headers: {
 5        'Authorization': `Token ${token}`,
 6        'Content-Type': 'application/json'
 7    }
 8})
 9.then(response => response.json())
10.then(data => console.log(data));

Создание API для получения токенов

Создай endpoint для аутентификации пользователей:

 1# views.py
 2from rest_framework import status
 3from rest_framework.decorators import api_view, permission_classes
 4from rest_framework.permissions import AllowAny
 5from rest_framework.response import Response
 6from rest_framework.authtoken.models import Token
 7from django.contrib.auth import authenticate
 8from django.views.decorators.csrf import csrf_exempt
 9
10@api_view(['POST'])
11@permission_classes([AllowAny])
12@csrf_exempt
13def login_view(request):
14    username = request.data.get('username')
15    password = request.data.get('password')
16
17    if username and password:
18        user = authenticate(username=username, password=password)
19        if user:
20            token, created = Token.objects.get_or_create(user=user)
21            return Response({
22                'token': token.key,
23                'user_id': user.id,
24                'username': user.username
25            })
26        else:
27            return Response({
28                'error': 'Неверные учетные данные'
29            }, status=status.HTTP_400_BAD_REQUEST)
30    else:
31        return Response({
32            'error': 'Укажите username и password'
33        }, status=status.HTTP_400_BAD_REQUEST)

URLs для токенной аутентификации

1# urls.py
2from django.urls import path
3from . import views
4
5urlpatterns = [
6    path('api/login/', views.login_view, name='api_login'),
7    path('api/profile/', views.UserProfileView.as_view(), name='api_profile'),
8    path('api/protected/', views.protected_view, name='api_protected'),
9]

Безопасность токенов

Важные моменты для безопасности:

1. Срок действия токенов

1# settings.py
2REST_FRAMEWORK = {
3    'DEFAULT_AUTHENTICATION_CLASSES': [
4        'rest_framework.authentication.TokenAuthentication',
5    ],
6    'TOKEN_EXPIRE_HOURS': 24 * 7,  # 7 дней
7}

2. Удаление токенов при выходе

1@api_view(['POST'])
2@authentication_classes([TokenAuthentication])
3@permission_classes([IsAuthenticated])
4def logout_view(request):
5    # Удаляем токен пользователя
6    request.user.auth_token.delete()
7    return Response({'message': 'Успешный выход'})

3. Проверка токенов в middleware

 1# middleware.py
 2from django.utils.deprecation import MiddlewareMixin
 3from rest_framework.authtoken.models import Token
 4
 5class TokenValidationMiddleware(MiddlewareMixin):
 6    def process_request(self, request):
 7        auth_header = request.META.get('HTTP_AUTHORIZATION', '')
 8        if auth_header.startswith('Token '):
 9            token_key = auth_header.split(' ')[1]
10            try:
11                token = Token.objects.get(key=token_key)
12                request.user = token.user
13            except Token.DoesNotExist:
14                request.user = None

Тестирование токенной аутентификации

 1# tests.py
 2  from django.test import TestCase
 3  from django.contrib.auth.models import User
 4  from rest_framework.test import APIClient
 5  from rest_framework.authtoken.models import Token
 6
 7  class TokenAuthenticationTest(TestCase):
 8      def setUp(self):
 9          self.client = APIClient()
10          self.user = User.objects.create_user(
11              username='testuser',
12              password='testpass123'
13          )
14          self.token = Token.objects.create(user=self.user)
15
16      def test_protected_endpoint_with_token(self):
17          self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
18          response = self.client.get('/api/profile/')
19          self.assertEqual(response.status_code, 200)
20
21      def test_protected_endpoint_without_token(self):
22          response = self.client.get('/api/profile/')
23          self.assertEqual(response.status_code, 401)

FAQ

Q: Как генерировать токены для пользователей?
A: Используй Token.objects.get_or_create(user=user) или создавай через админку. Можно автоматизировать через signals или management commands.

Q: Как долго действуют токены?
A: По умолчанию токены не имеют срока действия. Рекомендуется настроить автоматическое удаление старых токенов или использовать JWT токены.

Q: Можно ли использовать несколько токенов для одного пользователя?
A: По умолчанию DRF создает только один токен на пользователя. Для множественных токенов используй кастомную модель или JWT.

Q: Как безопасно передавать токены в frontend?
A: Храни токены в localStorage или sessionStorage, используй HTTPS, и реализуй механизм обновления токенов.

Q: Что делать если токен скомпрометирован?
A: Немедленно удали токен через Token.objects.filter(user=user).delete() и создай новый. Рассмотри использование JWT с коротким сроком действия.

Q: Как добавить дополнительную информацию в токен?
A: Расширь модель Token или используй связанные модели для хранения дополнительных данных пользователя.