在 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 是否认为它丢失了或者它是否认为它不正确,所以我不确定如何进行。连接调试器是不切实际的,因为我不知道我应该在哪个内部方法停止执行,所以我被卡住了。

编辑: 我最近尝试过的其他事情:

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

我发现了问题:我的设置中的 CSRF_HEADER_NAME = "X-CSRFToken" 没有考虑到 Django 的事实,“显式优于隐式”,隐式规范化所有 header 名称,例如请求中的令牌最终看起来像 HTTP_X_CSRFTOKEN,但不会对自定义名称做同样的事情,因此在 Django 比较它们时两者将不匹配。