Django Rest Framework не примет мой токен CSRF - PullRequest
0 голосов
/ 25 апреля 2019

Я пытаюсь создать одностраничное приложение с Django Rest Framework.Для аутентификации я использую вид входа в систему, который инициирует сеанс и требует защиты csrf на всех маршрутах API.Поскольку там нет шаблонов, тег csrf_token никогда не используется, поэтому мне нужно вручную получить токен с помощью get_token.Вместо того, чтобы помещать его в основной индексный файл, который будет отображаться в домашнем представлении, я хочу установить его в свой собственный файл cookie.Нет, это не файл cookie CSRF, который предоставляет django, поскольку у него есть секрет CSRF, плюс я упомянул, что я использую сеансы, поэтому секрет хранится там.Этот cookie будет иметь токен, который будет использоваться для всех изменяющихся запросов.

Я перепробовал все, чтобы django принял cookie, но ничего не помогло.Я могу нормально войти в первый раз, потому что предыдущей сессии не было, но что-то после этого просто выдает ошибку 403. Я пытался использовать ensure_csrf_cookie, но это не помогло.Я пробовал без сессий и до сих пор ничего.Я даже попытался изменить порядок промежуточного программного обеспечения и все еще ничего.Я даже попытался создать свое собственное промежуточное программное обеспечение, но оно не сработало.Вот код, с которым я закончил на данный момент:

views.py

@api_view(http_method_names = ["GET"])
def home(request):
    """API route for retrieving the main page of web application"""
    return Response(None, status = status.HTTP_204_NO_CONTENT);

class LoginView(APIView):
    """API endpoint that allows users to login"""
    def post(self, request, format = None):
        """API login handler"""
        user = authenticate(username = request.data["username"], password = request.data['password']);
        if user is None:
            raise AuthenticationFailed;
        login(request, user);
        return Response(UserSerializer(user).data);

class LogoutView(APIView):
    """API endpoint that allows users to logout of application"""
    def post(self, request, format = None):
        logout(request);
        return Response(None, status = status.HTTP_204_NO_CONTENT);

settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware'
]

# Sessions

SESSION_ENGINE = "django.contrib.sessions.backends.file"
SESSION_FILE_PATH = os.getenv("DJANGO_SESSION_FILE_PATH")
SESSION_COOKIE_AGE = int(os.getenv("SESSION_LIFETIME")) * 60
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_NAME = os.getenv("SESSION_COOKIE")
SESSION_COOKIE_SECURE = os.getenv("APP_ENV") != "local"

# CSRF
CSRF_USE_SESSIONS = True
# the following setting is for the csrf token only, not for the CSRF secret, which is the default for django
CSRF_TOKEN_CARRIER = os.getenv("XSRF_COOKIE")
CSRF_HEADER_NAME = "X-XSRF-TOKEN"
CSRF_COOKIE_SECURE = SESSION_COOKIE_SECURE
CSRF_COOKIE_AGE = SESSION_COOKIE_AGE
CSRF_TOKEN_HTTPONLY = False

REST_FRAMEWORK = {
    "EXCEPTION_HANDLER":"django_app.application.exceptions.global_exception_handler",
    "DEFAULT_AUTHENTICATION_CLASSES":[
        "rest_framework.authentication.SessionAuthentication"
    ]
}

это было пользовательское промежуточное ПО, которое я написално сейчас я не использую:

"""Custom CSRF Middleware for generating CSRF cookies"""
from django.conf import settings;
from django.middleware.csrf import CsrfViewMiddleware, rotate_token, get_token;

class CSRFCookieMiddleware:
    """Sets CSRF token cookie for ajax requests"""
    def __init__(self, get_response):
        self.get_response = get_response;

    def __call__(self, request):
        response = self.get_response(request);
        if settings.CSRF_USE_SESSIONS:
            response.set_cookie(
                settings.CSRF_TOKEN_CARRIER,
                get_token(request),
                max_age=settings.CSRF_COOKIE_AGE,
                domain=settings.CSRF_COOKIE_DOMAIN,
                path=settings.CSRF_COOKIE_PATH,
                secure=settings.CSRF_COOKIE_SECURE,
                httponly=settings.CSRF_COOKIE_HTTPONLY,
                samesite=settings.CSRF_COOKIE_SAMESITE,
            );
        return response;

ответ об ошибке 403:

HTTP/1.1 403 Forbidden
Date: Thu, 25 Apr 2019 17:11:28 GMT
Server: WSGIServer/0.2 CPython/3.7.0
Content-Type: application/json
Vary: Accept, Cookie
Allow: POST, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Length: 59

{
  "message": "CSRF Failed: CSRF token missing or incorrect."
}

это HTTP-запрос, который я использую в своем клиенте REST в коде:

POST http://electro:8000/api/logout
X-XSRF-TOKEN: JFaygAm49v6wChT6CcUJaeLwq53YwzAlnEZmoE0m21cg9xLCnZGvTt6oM9MKbvV8
Cookie: electro=nzzv64gymt1aqu4whdhax1s9t91c3m58


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

1 Ответ

0 голосов
/ 02 мая 2019

Я наконец понял, что случилось. Глубоко в документации django я обнаружил, что параметр CSRF_HEADER_NAME имеет определенный синтаксис / формат:

# default value
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN";

, поэтому, чтобы исправить это, в документах буквально говорится, что для моегоcase я должен установить значение в соответствии с моими предпочтениями, например так:

CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN";

Так что теперь он может принять токен в заголовке X-XSRF-TOKEN вместе с cookie-файлом сессии.Но поскольку я использую сеансы с csrf, я должен использовать созданное мной специальное промежуточное программное обеспечение (см. Вопрос), чтобы вручную установить cookie-токен csrf.Это связано с тем, что sure_csrf_cookie, по-видимому, только генерирует вам файл cookie сеанса.

Наконец, если вам нужно защитить маршрут входа в систему, так как я использую SessionAuthentication, мне потребуется пользовательское промежуточное ПО, ensure_csrf_cookie, иcsrf_protect чтобы я мог получить начальный сеанс с токеном csrf и затем отправить его при входе в систему:

@api_view(http_method_names = ["GET"])
@ensure_csrf_cookie
def home(request):
    """API route for retrieving the main page of web application"""
    return Response(None, status = status.HTTP_204_NO_CONTENT);

@method_decorator(csrf_protect, 'dispatch')
@method_decorator(ensure_csrf_cookie, 'dispatch')
class LoginView(APIView):
    """API endpoint that allows users to login"""
    def post(self, request, format = None):
        """API login handler"""
        user = authenticate(username = request.data["username"], password = request.data['password']);
        if user is None:
            raise AuthenticationFailed;
        login(request, user);
        return Response(UserSerializer(user).data);

может ли это помочь любому, кто создает бэкэнд одностраничного приложения с помощью django

...