在 Django + Vue 设置中不断获得 403 "CSRF token missing or incorrect"



我搜索过其他类似的问题,但没有一个解决方案奏效,也没有让我深入了解可能发生的事情。

我的设置是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令牌的cookiecsrftoken,它似乎工作正常,正如我在devtools中看到的那样,如果我删除了它,当我刷新时它会再次出现。以下是我的相关Djangosettings.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方面(Typescript(,我使用以下内容发送请求:

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),
},
)

这也可以按预期工作,正如我在devtools中看到的那样,请求确实包含X-CSRFToken标头,其内容与csrftokencookie相同。然而,响应仍然是403,声称令牌丢失或不正确。我不确定Django是认为它丢失了,还是认为它不正确,所以我不确定如何继续。连接调试器是不切实际的,因为我不知道应该在什么内部方法停止执行,所以我陷入了困境。

编辑:我最近尝试过的其他东西:

  • ,在我的主视图上用@ensure_csrf_cookie装饰器替换了在我的自定义渲染函数中使用get_token()。奇怪的是,这并没有导致按预期设置csrf cookie
  • 再次在后端,将@csrf_protect装饰器添加到主视图中。这个确实导致csrf cookie被设置,尽管我不太明白为什么。无论如何,这并没有解决问题
  • 在前端,将一个csrfmiddlewaretoken字段添加到POST请求的主体中,并将令牌作为值
  • 再次在前端,获取Django文档中显示的具有完全相同功能的令牌,而不是我的自定义令牌

不幸的是,这些都没有改变结果:当我有可用的cookie时,它的值似乎不能满足Django。

我发现了问题:我的设置中的CSRF_HEADER_NAME = "X-CSRFToken"没有考虑到Django的事实;"显式优于隐式";,隐式地规范化所有标头名称,这样请求中的令牌最终会看起来像HTTP_X_CSRFTOKEN,但不会对自定义名称进行同样的处理,因此当Django比较它们时,两者不会匹配。

最新更新