Получение 401 unauthorized при попытке доступа к странице с JWT-аутентификацией в Django даже после генерации токена

Я студент, пытающийся обучить себя JWT-аутентификации. Я создал базовую страницу входа в систему, и после входа в систему пользователь должен увидеть страницу приветствия, на которой написано Welcome {username}. Однако даже после входа в систему на странице приветствия написано

Приветствие GET /welcome/ HTTP 401 Неавторизованный Разрешить: GET, OPTIONS Content-Type: application/json Vary: Accept WWW-Authenticate: Bearer realm="api"

{ "detail": "Учетные данные для аутентификации не были предоставлены". }

Мой код выглядит следующим образом:

views.py

from django.http import JsonResponse
from django.shortcuts import render, redirect
from loginapp.models import User
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import authenticate
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.urls import reverse


def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print("Username:", username)
        print("Password:", password)
        user = User.objects.filter(username=username).first()
        #user = authenticate(username=username, password=password)
        if user is not None and user.password == password:
            refresh = RefreshToken.for_user(user)
            token = str(refresh.access_token)
            response = JsonResponse({'message': 'Login successful'})
            response['Authorization'] = f'Bearer {token}'
            return response
        else:
            return JsonResponse({'error_message': 'Invalid username or password.'}, status=400)
    else:
        return render(request, 'login.html')

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def welcome(request):
    username = request.user.username
    return Response({'message': f'Welcome, {username}!'})

мой urls.py выглядит следующим образом:

from django.urls import path
from loginapp import views
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.views import TokenRefreshView


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.login, name='login'),
    path('welcome/',views.welcome, name='welcome'),
    #path('welcome/<str:username>/', views.welcome, name='welcome'),
    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

my login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <div id="error-message"></div>
    <form id="loginForm">
        {% csrf_token %}
        <label for="username">Username:</label>
        <input type="text" id="username" name="username"><br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password"><br>
        <button type="submit">Login</button>
    </form>

    <script>
        document.getElementById("loginForm").addEventListener("submit", function(event) {
            event.preventDefault();
            
            fetch("/", {
                method: "POST",
                body: new FormData(document.getElementById("loginForm"))
            })
            .then(response => {
                if (response.ok) {
                    const token = response.headers.get('Authorization').split(' ')[1];
                    localStorage.setItem("jwtToken", token);
                    window.location.href = "/welcome";
                } else {
                    return response.json();
                }
            })
            .then(data => {
                if (data.error_message) {
                    document.getElementById("error-message").innerText = data.error_message;
                }
            })
            .catch(error => {
                console.error("Error:", error);
            });
        });
    </script>
</body>
</html>

my 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=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': True,
}

Может ли кто-нибудь точно определить, где может возникнуть проблема? Я использую базу данных MySQL, из которой я запрашиваю имя пользователя.

Я попробовал изменить в settings.py ACCESS_TOKEN_LIFETIME, но даже это уже 60 минут, а ошибка все еще сохраняется.

Вы вернули JsonResponse с токеном доступа внутри него, но после этого вам придется вручную добавить значение токена в заголовок следующего запроса.

response = JsonResponse({'message': 'Login successful'})
response['Authorization'] = f'Bearer {token}'
return response

Этот код не будет работать должным образом, поскольку каждый запрос и ответ рассматриваются как независимые. Ваш следующий запрос не будет содержать токен авторизации, что приведет к ошибке 401 Unauthorized.

Я рекомендую возвращать только токен доступа, не задавая Authorization ключ для ответа.

return JsonResponse({'message': 'Login successful', 'token': token})

И вы можете использовать этот токен, сохранив его в cookie или локальном хранилище во фронтенде.

Вернуться на верх