CORS 在 Django 中不起作用,但设置似乎正确

Posted

技术标签:

【中文标题】CORS 在 Django 中不起作用,但设置似乎正确【英文标题】:CORS not working in Django but settings seem correct 【发布时间】:2021-07-23 10:17:54 【问题描述】:

我正在尝试从不同子域上的 React Native Web 前端对 Django 进行 POST 调用。

我以为我已经正确配置了 CORS,但事实并非如此。

这是我的 Django settings.py 的样子:

CORS_ALLOW_CREDENTIALS = True

CORS_ALLOW_HEADERS = ['*']

CORS_ALLOWED_ORIGINS = ['https://api.example.com', 'https://example.com', 'https://www.example.com' ]

CSRF_TRUSTED_ORIGINS = [
    'https://api.example.com', 'https://example.com', 'https://www.example.com'
]

ALLOWED_HOSTS = ["0.0.0.0", "api.example.com", "example.com"]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
]

INSTALLED_APPS = [
     ...
    'corsheaders',
     ...
]

我到底做错了什么?我得到的错误是这样的:

Access to XMLHttpRequest at 'https://api.example.com/api/v1/pagescreate/' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这是我的 Django 视图:

class PageCreateView(generics.CreateAPIView):
    queryset = Page.objects.all()
    serializer_class = PageSerializer

这可能是什么原因造成的?我错过了 React 中的一些设置吗?我正在使用 axios 进行调用,唯一的标题是 "Content-Type": "application/json"

编辑:这可能是由于我的服务器上的一些 nginx 规则造成的吗?或者我的 Kubernetes 配置?我正在使用 Docker 来设置容器,并且可以轻松链接 Dockerfile 或我的 Kubernetes 设置中的任何信息

【问题讨论】:

这个问题你解决了吗? @MahmoudAdel 我没有,没有 我认为如果有人有更好的答案可能会更好,我之前遇到过这个问题,但通过正确设置我的 conf 解决了它,我不知道为什么你的不工作,但所有我能做的就是分享我的 Django conf,对于react,它可能是缺少标头问题 @MahmoudAdel 是的,我也怀疑缺少标题。我可以在两个小时内悬赏,所以我可能最终会这样做 也许这个答案有帮助? ***.com/a/50949631/4984493 【参考方案1】:

我认为您可能缺少几个中间件:

django.middleware.csrf.CsrfViewMiddleware
corsheaders.middleware.CorsPostCsrfMiddleware

否则我相信你的 CORS_ALLOWED_ORIGINS 和 CSRF_TRUSTED_ORIGINS 设置没有问题

【讨论】:

【参考方案2】:

我有一个类似的问题,通过将corsheaders.middleware.CorsMiddleware 移到django.middleware.common.CommonMiddleware 上方,在MIDDLEWARE 数组(settings.py)中解决了。

【讨论】:

也许你可以把你的答案写在代码里。这对于其他用户来说将更具可读性和可用性。【参考方案3】:

我做了一些与你正在做的非常相似的事情,这个配置对我有用:

INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    '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',
]

CORS_ORIGIN_WHITELIST = [
     'https://website.com'
]

【讨论】:

【参考方案4】:

我在 Django 上遇到了一大堆 CORS 问题。一般来说,你可以尝试使用:

CORS_ALLOWED_ORIGINS = ['*']

CSRF_TRUSTED_ORIGINS = ['*']

(注意:这只是样板文件,您可能不想在生产中这样做;最终必须找到实际问题)

确保它在您的 Django 设置中。但是,正如我在这里提到的:What can cause Chrome to give an net::ERR_FAILED on cached content against a server on localhost? 有各种各样的事情会导致 CORS 错误(有时甚至没有将它们注册为 CORS 错误),并且它可能来自各种位置。真正的问题最好通过挖掘日志来解决。 (注意:this answer 展示了如何将您的 Django 日志放入文件中,您可能应该这样做。标准的 Django REST 响应启动日志将包含有用的信息)

第一步是查看一个特定的请求是否正在访问您的 Django 日志。如果是这样,那么您在 Django 中的 CORS 设置就是问题所在。您可以很容易地判断它被拒绝的原因,因为 Django 将拥有它在日志中拒绝的完全限定 (MYSUBDOMAIN.example.com) 域。这使得更改 Django 设置变得轻而易举;只需将被拒绝的域添加到您允许的来源即可。

注意:如果您不是 BASH / Django 专业人士,请转到 Django 填充其日志并执行 ls -alrt 的日志目录(应在您的 settings.py 中引用),将为您提供最后修改的日志。

不过,我一直在 Ubuntu 上的 Apache2 中调试过这个问题。所以我在/var/log/apache2 有一个日志,它准确地显示了哪个域被拒绝以及为什么被拒绝。 (当 Apache 拒绝请求时;如果您将 Web 服务器放在 Django 前面,它也可能有 CORS 策略和插件)如果您在 docker 上使用 ngnix,以下问题 (Where are logs of docker nginx conainter stored in host) 建议您使用此命令行命令从 docker 中获取日志文件并查看它们在做什么。

docker inspect --format='.LogPath' <nginx-containter-id>

我个人通常最终会从命令行启动 Docker 实例并转到 BASH 提示符进行调试。对此的建议不在此问题的范围内。

更进一步,这个 CORS 策略问题可能在您的网络堆栈上更远。有时,Chrome 会将安全策略配置错误、重定向错误或其他与基础架构相关的问题误解为 CORS 问题。在某些情况下,CORS 解释甚至可能是浏览器配置问题。有时,我启动了一台虚拟机,将我在 Chrome 中的安全级别设置为绝对最低,然后尝试一下,看看 CORS 是否真的是问题所在。

有时不是。有时是其他东西——负载均衡器、防火墙、错误配置的 AWS 安全组——才是罪魁祸首。

所以,是的,挖掘这个可能是一次完整的错误之旅,而不是简单的狩猎。但检查first 的核心是您的Django 日志是否在每个REST 请求之后吐出任何内容。如果他们不是,你需要上链,因为你根本没有和 Django 交谈。

啊,另一个注意事项:当 Django 无法写入其日志文件时,我们会收到 CORS 错误。

【讨论】:

不知道为什么这被否决了。它复制了其他一些答案,但它们并不完整,因为它们没有涵盖 CORS 通常是一个不准确的标签。我花了数周的时间在 Django 中研究 Chrome 称为 CORS 的问题;它们通常不是 CORS 或 Django,而是埋在操作系统的某个地方。【参考方案5】:

你可以试试:

CORS_ORIGIN_WHITELIST = ( 'https://example.com')

【讨论】:

【参考方案6】:

CORS_ALLOW_CREDENTIALS = True 使 CORS_ALLOW_HEADERS = ['*'] 被直译,即不是通配符。

如果要添加自定义标题,则需要扩展默认列表,就像在 doc 中所做的那样;

from corsheaders.defaults import default_headers

CORS_ALLOW_HEADERS = list(default_headers) + [
    'my-custom-header',
]

由于请求具有 Content-Typeapplication/json,因此根据 MDN,这不是 simple request。因此,此请求会触发包含 Access-Control-Request-Headers 请求标头的 CORS 预检请求。此请求标头引出Access-Control-Allow-Headers 响应标头。

此响应标头的值可以是*,以指示缺少凭据时的通配符值。但是当请求中存在 HTTP cookie 或身份验证信息时,* 会按字面意思解释,就像这里使用 CORS_ALLOW_CREDENTIALS = True 所做的那样。

在 CORS 预检中 Access-Control-Request-Headers 的值为 Content-TypeAccess-Control-Allow-Headers 是文字 * 因为 CORS_ALLOW_HEADERS = ['*']。所以请求没有被授权,这就是为什么错误 No 'Access-Control-Allow-Origin' header is present on the requested resource.

GET 在这里工作,因为它是一个“简单请求”并且不会触发 CORS perflight。

CORSAccess-Control-Allow-Headers

【讨论】:

【参考方案7】:

我认为,这可能是由于设置中的中间件顺序造成的。 (虽然我并不完全自信)

你能不能试试下面的中间件序列

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

然后删除

CORS_ALLOW_HEADERS = ['*']

来自设置。

如果这行得通,请告诉我

【讨论】:

【参考方案8】:

如果您在 K8s 中运行它,请查看在 ALLOWED_HOSTS 中包含以下内容是否有帮助:

gethostname(), gethostbyname(gethostname())

【讨论】:

【参考方案9】:

免责声明:不是 django 开发者。

但是您使用non-whitelisted value 表示Content-Type,这意味着您的POST 请求将是"preflighted"。您能否确认您的 API 正确处理了 OPTIONS 请求?

【讨论】:

【参考方案10】:

我之前遇到过这个问题,我建议使用:

CORS_ORIGIN_ALLOW_ALL = True   

这将允许所有来源

当然有这个配置:

MIDDLEWARE = [
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

INSTALLED_APPS = [
     ...
    'corsheaders',
     ...
]

【讨论】:

虽然这在短期内是一个很好的答案,但它确实允许跨站点脚本,这是 CSRF 应该防止的。深入了解您的日志并准确找出您拒绝的合格域是什么,然后将这些域添加到您允许的来源不会留下巨大的安全漏洞。 @steven-matthews 绕过 CORS 是不好的做法。虽然我没有测试我希望我已经在我的回答中指出了问题***.com/a/67453601/4522780【参考方案11】:

正如 here 解释的那样,CORS Access-Control-Allow-Origin 行需要以下两种格式之一:

允许一切:可能不是你想要的

Access-Control-Allow-Origin: *

允许单个特定来源:

Access-Control-Allow-Origin: [SCHEME]://[HOST]:[PORT_OPTIONAL]

Scheme:AKA 协议,可以是 HTTP、HTTPS 。 主机:这必须与请求源完全匹配,因此 subdomain.domain.com 或 domain.com 端口:[可选,但对您很重要] 如果您的方案是 HTTP,则保留端口与放置默认端口相同:80 如果您的方案是 HTTPS,则放置默认端口:443。

此外,您向其发出请求的服务器 https://example.com 必须实施 CORS 以从您的网站授予脚本访问权限。您的脚本无法授予自己访问其他网站的权限。

我使用了来自 chrome 网上商店的名为 allow-cors-access-control 的 CORS 扩展。

或者,您可以像这样使用 change-origin chrome 插件:

Moesif Orign & CORS Changer

您可以使您的本地开发服务器 (ex: localhost:8080) 看起来来自 172.16.1.157:8002 或任何其他域。

【讨论】:

【参考方案12】:

您确定 CORS_ALLOW_HEADERS 允许您设置“*”吗?

基于这里,它看起来像是被设计为扩展而不是替换: https://github.com/adamchainz/django-cors-headers/blob/2d22268ff59aa0b79820ea5b8983efd1e514ec4c/README.rst#cors_allow_headers 这个测试也让我觉得需要扩展而不是替换 https://github.com/adamchainz/django-cors-headers/blob/5067faefeb22beffcf1b12dd7ad9d3fb6d4b26e4/src/corsheaders/checks.py#L20-L21

我建议尝试使用默认值或尝试从上面的链接扩展它

我认为这是特定于库的值,而不是浏览器或请求 CORS 值

编辑:添加到 github.com 的测试链接

【讨论】:

['*'] 是 1 个字符串的序列(子类型:列表)。

以上是关于CORS 在 Django 中不起作用,但设置似乎正确的主要内容,如果未能解决你的问题,请参考以下文章

CORS 同步请求在 Firefox 中不起作用

使用 CORS 的应用程序在 Azure 中不起作用

使用 Ajax 的按钮在 Django 项目中不起作用

对 API 网关的 CORS 请求在浏览器中不起作用

自定义激活电子邮件在 Django 中不起作用

gzip 在带有 Whitenoise 的 Django 中不起作用