JavaRush /Курси /Модуль 3: Django /Використання JWT для автентифікації користувачів

Використання JWT для автентифікації користувачів

Модуль 3: Django
Рівень 18 , Лекція 6
Відкрита

Раніше ми розібралися з основними концепціями автентифікації, зрозуміли, що таке токени та їхні переваги, а також налаштували інтеграцію токенів і JWT (JSON Web Tokens) в проєкт Django. Ти вже знаєш, як встановити потрібні бібліотеки та виконати мінімальну конфігурацію проєкту для роботи з JWT. Давай перейдемо до практики. І ось що ми будемо робити:

  1. Створювати JWT для користувачів під час їхнього входу в систему.
  2. Перевіряти JWT у кожному запиті при зверненні до API.
  3. Реалізовувати логіку входу (login) і виходу (logout) на основі JWT.
  4. Забезпечувати безпеку API та захист даних.

Швидке нагадування: що таке JWT?

JWT (JSON Web Token) — це компактний і безпечний спосіб передачі інформації між сторонами, який використовується для автентифікації та обміну даними.

Приклад JWT складається з трьох частин, розділених крапками:

header.payload.signature
  • Header: заголовок, який містить тип токена та алгоритм шифрування.
  • Payload: корисні дані, наприклад, інформація про користувача (id, ролі та інші дані).
  • Signature: підпис, який генерується з використанням секрета і забезпечує захист даних від змін.

Детальніше про пристрій JWT можна почитати тут.

Створення і видача JWT

Що ми будемо робити? При успішному вході користувача ми будемо генерувати JWT, який клієнт зможе використовувати для звернення до твоїх API.

Встановлення бібліотеки djangorestframework-simplejwt

Для роботи з JWT нам знадобиться стороння бібліотека, яка спрощує створення і обробку токенів:

pip install djangorestframework-simplejwt

У settings.py додай налаштування для SimpleJWT:

from datetime import timedelta

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

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'SIGNING_KEY': 'tviy_sekretnyy_klyuch',
}
👀 Підказка:

використовуй безпечний секретний ключ і не зберігай його в коді. Краще винести його в змінні середовища з використанням, наприклад, бібліотеки python-decouple.

Ендпоінти для входу і отримання токена

Тепер нам потрібно визначити два ендпоінти:

  1. /api/token/ — для отримання токена при логіні.
  2. /api/token/refresh/ — для оновлення токена.

Для цього додамо маршрути в urls.py твого додатка:

from django.urls import path
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Тепер, якщо ми надішлемо POST-запит на /api/token/, вказавши правильні username і password, ми отримаємо access- і refresh-токени. Ось приклад запиту:

POST /api/token/
Content-Type: application/json

{
    "username": "demo_user",
    "password": "12345678"
}

Приклад відповіді:

{
    "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Використання JWT для захисту ендпоінтів

Тепер, коли ми вміємо генерувати токени, давайте налаштуємо ваші API так, щоб вони перевіряли JWT у кожному запиті.

Давайте наведемо приклад захищеного представлення. Створимо API, доступ до якого буде тільки у авторизованих користувачів:

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

class ProtectedAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response({"message": "Привіт, авторизований користувач!"})

Додамо маршрут для цього API у urls.py:

from django.urls import path
from .views import ProtectedAPIView

urlpatterns = [
    path('api/protected/', ProtectedAPIView.as_view(), name='protected_api'),
]

Тепер, якщо ми спробуємо зробити запит до /api/protected/ без токена, сервер поверне помилку 401 Unauthorized:

{
    "detail": "Облікові дані не були надані."
}

Щоб отримати доступ, необхідно передати JWT у заголовок запиту:

Authorization: Bearer <ваш_access_токен>

І сервер відповість:

{
    "message": "Привіт, авторизований користувач!"
}

Вихід (Logout)

JWT за своєю природою не надають повноцінної можливості "вийти з системи" користувача, оскільки токен залишається дійсним до закінчення терміну його дії. Однак ми можемо використовувати механізм чорного списку для відкликання токенів.

Додамо можливість роботи з чорним списком:

pip install djangorestframework-simplejwt[blacklist]

У settings.py підключимо додаток rest_framework_simplejwt.token_blacklist:

INSTALLED_APPS += [
    'rest_framework_simplejwt.token_blacklist',
]

Далі необхідно виконати міграції:

python manage.py migrate

Тепер ми можемо створити ендпоінт для відкликання токенів:

from rest_framework_simplejwt.tokens import OutstandingToken, BlacklistedToken
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated

class LogoutView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            # Відкликаємо всі токени користувача
            for token in OutstandingToken.objects.filter(user=request.user):
                BlacklistedToken.objects.create(token=token)
            return Response({"message": "Ви успішно вийшли з системи"}, status=200)
        except Exception as e:
            return Response({"error": str(e)}, status=400)

Маршрут для виходу:

path('api/logout/', LogoutView.as_view(), name='logout'),

Тепер, викликавши /api/logout/ з токеном у заголовку, всі токени цього користувача будуть анульовані.

Додаткові можливості: кастомізація токенів

Ви можете додати користувацькі дані у payload токена. Для цього перевизначте TokenObtainPairSerializer:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

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

        # Додаємо користувацькі поля
        token['email'] = user.email
        token['is_staff'] = user.is_staff

        return token

І вкажіть новий серіалізатор у вашому представленні:

from rest_framework_simplejwt.views import TokenObtainPairView

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

Не забудьте оновити маршрут:

path('api/token/', CustomTokenObtainPairView.as_view(), name='custom_token_obtain_pair'),

Тепер у виданих токенах будуть додаткові дані.

Підсумок

Ми налаштували JWT для аутентифікації користувачів, навчилися генерувати токени та перевіряти запити на їхню справжність. Додали ендпоінти для входу, виходу та захисту API. Це потужний інструмент, який часто використовується в сучасних веб-додатках, особливо при інтеграції з мобільними додатками або сторонніми сервісами.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ