简体   繁体   English

Django Rest Framework不接受我的CSRF令牌

[英]Django Rest Framework will not accept my CSRF Token

I'm trying to build a Single Page Application with Django Rest Framework. 我正在尝试使用Django Rest Framework构建单页面应用程序。 For authentication, I'm using a login view that initiates a session and requires csrf protection on all api routes. 对于身份验证,我正在使用启动会话的登录视图,并且需要对所有api路由进行csrf保护。 Because there is no templating going on, the csrf_token tag is never used, so I have to manually get the token with get_token . 因为没有模板化,所以从不使用csrf_token标记,因此我必须使用get_token手动获取标记。 Instead of putting it in the main index file that will be given in the home view, I want to set it on its own cookie. 我想把它放在自己的cookie上,而不是把它放在主视图中给出的主索引文件中。 No this is not the CSRF Cookie that django provides, as that one has the CSRF secret, plus I mentioned I'm using sessions, so the secret is stored there. 不,这不是django提供的CSRF Coo​​kie,因为那个拥有CSRF秘密,而且我提到我正在使用会话,因此秘密存储在那里。 This cookie will have the token which will be used for all mutating requests. 此cookie将具有将用于所有变更请求的令牌。

I have tried everything to get django to accept the cookie but nothing has worked. 我已经尝试了一切让django接受cookie但没有任何效果。 I can login just fine the first time, because there was no previous session, but anything after that just throws an error 403. I tried using ensure_csrf_cookie but that didn't help. 我可以第一次登录,因为没有以前的会话,但之后的任何事情只会引发错误403.我尝试使用ensure_csrf_cookie但这没有帮助。 I tried without sessions and still nothing. 我试过没有会话,但仍然没有。 I even tried rearranging the middleware order and still nothing. 我甚至尝试重新安排中间件订单,但仍然没有。 I even tried my own custom middleware to create the cookie but it didn't work. 我甚至尝试了自己的自定义中间件来创建cookie,但它没有用。 Here's the code that I have ended up with as of now: 这是我到目前为止的代码:

views.py 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 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"
    ]
}

this was the custom middleware I wrote but I'm not using right now: 这是我写的自定义中间件,但我现在没有使用:

"""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;

the error 403 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."
}

this is the http request I use in my REST client in vs code: 这是我在我的REST客户端中使用的http请求vs代码:

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


I cannot believe how hard it is to tweak frameworks to work with single page apps when there's plenty support for static websites and APIs. 当有足够的静态网站和API支持时,我无法相信调整框架以使用单页应用程序是多么困难。 So where have I gone wrong? 那么我哪里出错了?

I finally figured out what happened. 我终于弄清楚发生了什么。 Buried deep in the django documentation , I found out that the CSRF_HEADER_NAME setting has a specific syntax/format: 深入了解django文档 ,我发现CSRF_HEADER_NAME设置具有特定的语法/格式:

# default value
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN";

so to fix this, the docs literally say that for my case I must set the value, according to my preferences, like so: 为了解决这个问题,文档字面上说,对于我的情况,我必须根据我的喜好设置值,如下所示:

CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN";

So now it can accept the token at X-XSRF-TOKEN header, along with session cookie. 所以现在它可以接受X-XSRF-TOKEN标头上X-XSRF-TOKEN以及会话cookie。 But since I'm using sessions with csrf, I must use the custom middleware I created (see question) to set the csrf token cookie manually. 但由于我正在使用csrf的会话,我必须使用我创建的自定义中间件(请参阅问题)手动设置csrf令牌cookie。 This is because ensure_csrf_cookie apparently only throws you the session cookie. 这是因为ensure_csrf_cookie显然只会抛出会话cookie。

Lastly, if you need to protect the login route, since I'm using SessionAuthentication , I will need the custom middleware, ensure_csrf_cookie , and csrf_protect so that I can get a starting session with csrf token and then submit those when logging in: 最后,如果您需要保护登录路由,因为我正在使用SessionAuthentication ,我将需要自定义中间件, ensure_csrf_cookiecsrf_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);

may this help whoever is building a Single Page App backend with django 这可能有助于任何使用django构建单页应用程序后端的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 有什么方法可以在django_rest_framework中使用csrf_token而无需在django中使用前端? - Is there any way for using csrf_token in django_rest_framework without using front end in django? Django / Django Rest Framework - 禁用CSRF - Django/Django Rest Framework - Disable CSRF 无法使用Django rest框架提交发布请求(“详细信息”:“ CSRF失败:CSRF令牌丢失或不正确。”) - Can't submit a post request using Django rest framework (“detail”: “CSRF Failed: CSRF token missing or incorrect.”) 在 django rest 框架(使用 TokenAuthentication)公共 APIView 上获取 CSRF 令牌丢失错误 - Getting CSRF token missing error on a django rest framework (with TokenAuthentication) public APIView Django Rest Framework抱怨CSRF - Django Rest Framework complaining about CSRF CSRF 豁免失败 - APIView csrf django rest 框架 - CSRF Exempt Failure - APIView csrf django rest framework python Django REST 框架 CSRF 失败:CSRF cookie 未设置? - python Django REST Framework CSRF Failed: CSRF cookie not set? Django REST Framework CSRF 失败:CSRF cookie 未设置 - Django REST Framework CSRF Failed: CSRF cookie not set Django REST框架验证令牌 - Django REST Framework Auth Token Django Rest 框架令牌认证 - Django Rest Framework Token Authentication
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM