在 Django + Vue 设置中继续获得 403 "CSRF token missing or incorrect"
Keep getting 403 "CSRF token missing or incorrect" in Django + Vue setup
我已经搜索过其他类似的问题,但 none 的解决方案有效,也没有让我深入了解可能发生的事情。
我的设置是一个 Vue 前端(有自己的路由)加上一个 Django 后端和 API。我尝试的任何 GET 路由都按预期工作,但 POST 需要 CSRF 保护。我有一个自定义渲染函数,我调用路由到索引(然后将由 Vue 处理),我在其中提供 CSRF 令牌,如下所示:
def custom_render(request):
# ...
# from django.middleware.csrf
get_token(request)
# from django.shortcuts
return render(request, template)
这设置了一个带有 CSRF 令牌的 cookie csrftoken
,它似乎工作正常,正如我在开发工具中看到的那样,如果我删除它,当我刷新时它又会出现。这是我的相关 Django settings.py
:
# This one is True in production, but for now I'm testing locally
CSRF_COOKIE_SECURE = False
CSRF_HEADER_NAME = "X-CSRFToken"
# I tried playing with both these options' values to no avail
CSRF_USE_SESSIONS = False
CSRF_COOKIE_HTTPONLY = False
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",
"django.middleware.common.BrokenLinkEmailsMiddleware",
]
在 Vue 端(打字稿)我使用这个发送请求:
function getCsrfToken() {
return document.cookie.match("(^|;)\s*" + "csrftoken" + "\s*=\s*([^;]+)")?.pop()
}
fetch(
"api/my-route/",
{
method: "POST",
headers: {
"X-CSRFToken": getCsrfToken(),
"Content-Type": "application/json",
"Accept": "application/json",
},
mode: "same-origin",
body: JSON.stringify(dataToSend),
},
)
这也按预期工作,正如我在开发工具中看到的那样,请求确实包含 X-CSRFToken
header,其内容与 csrftoken
cookie 相同。但是,响应是 nonetheless 403 声称令牌丢失或不正确。我不确定 Django 是否认为它丢失了或者它是否认为它不正确,所以我不确定如何进行。连接调试器是不切实际的,因为我不知道我应该在哪个内部方法停止执行,所以我被卡住了。
编辑: 我最近尝试过的其他事情:
- 在后端,用主视图上的
@ensure_csrf_cookie
装饰器替换我自定义渲染函数中 get_token()
的使用。奇怪的是,这 而不是 导致 csrf cookie 按预期设置。
- 再次在后端,将
@csrf_protect
装饰器添加到主视图。 did 导致设置了 csrf cookie,但我不太明白为什么。无论如何,这并没有解决问题。
- 在前端,在POST个请求的body中添加一个
csrfmiddlewaretoken
字段,以token为值。
- 再次在前端,获取与 Django 文档中显示的功能完全相同的令牌,而不是我自定义的。
不幸的是 none 这些改变了结果:当我有可用的 cookie 时,它的值似乎不满足 Django。
我发现了问题:我的设置中的 CSRF_HEADER_NAME = "X-CSRFToken"
没有考虑到 Django 的事实,“显式优于隐式”,隐式规范化所有 header 名称,例如请求中的令牌最终看起来像 HTTP_X_CSRFTOKEN
,但不会对自定义名称做同样的事情,因此在 Django 比较它们时两者将不匹配。
我已经搜索过其他类似的问题,但 none 的解决方案有效,也没有让我深入了解可能发生的事情。
我的设置是一个 Vue 前端(有自己的路由)加上一个 Django 后端和 API。我尝试的任何 GET 路由都按预期工作,但 POST 需要 CSRF 保护。我有一个自定义渲染函数,我调用路由到索引(然后将由 Vue 处理),我在其中提供 CSRF 令牌,如下所示:
def custom_render(request):
# ...
# from django.middleware.csrf
get_token(request)
# from django.shortcuts
return render(request, template)
这设置了一个带有 CSRF 令牌的 cookie csrftoken
,它似乎工作正常,正如我在开发工具中看到的那样,如果我删除它,当我刷新时它又会出现。这是我的相关 Django settings.py
:
# This one is True in production, but for now I'm testing locally
CSRF_COOKIE_SECURE = False
CSRF_HEADER_NAME = "X-CSRFToken"
# I tried playing with both these options' values to no avail
CSRF_USE_SESSIONS = False
CSRF_COOKIE_HTTPONLY = False
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",
"django.middleware.common.BrokenLinkEmailsMiddleware",
]
在 Vue 端(打字稿)我使用这个发送请求:
function getCsrfToken() {
return document.cookie.match("(^|;)\s*" + "csrftoken" + "\s*=\s*([^;]+)")?.pop()
}
fetch(
"api/my-route/",
{
method: "POST",
headers: {
"X-CSRFToken": getCsrfToken(),
"Content-Type": "application/json",
"Accept": "application/json",
},
mode: "same-origin",
body: JSON.stringify(dataToSend),
},
)
这也按预期工作,正如我在开发工具中看到的那样,请求确实包含 X-CSRFToken
header,其内容与 csrftoken
cookie 相同。但是,响应是 nonetheless 403 声称令牌丢失或不正确。我不确定 Django 是否认为它丢失了或者它是否认为它不正确,所以我不确定如何进行。连接调试器是不切实际的,因为我不知道我应该在哪个内部方法停止执行,所以我被卡住了。
编辑: 我最近尝试过的其他事情:
- 在后端,用主视图上的
@ensure_csrf_cookie
装饰器替换我自定义渲染函数中get_token()
的使用。奇怪的是,这 而不是 导致 csrf cookie 按预期设置。 - 再次在后端,将
@csrf_protect
装饰器添加到主视图。 did 导致设置了 csrf cookie,但我不太明白为什么。无论如何,这并没有解决问题。 - 在前端,在POST个请求的body中添加一个
csrfmiddlewaretoken
字段,以token为值。 - 再次在前端,获取与 Django 文档中显示的功能完全相同的令牌,而不是我自定义的。
不幸的是 none 这些改变了结果:当我有可用的 cookie 时,它的值似乎不满足 Django。
我发现了问题:我的设置中的 CSRF_HEADER_NAME = "X-CSRFToken"
没有考虑到 Django 的事实,“显式优于隐式”,隐式规范化所有 header 名称,例如请求中的令牌最终看起来像 HTTP_X_CSRFTOKEN
,但不会对自定义名称做同样的事情,因此在 Django 比较它们时两者将不匹配。