django-cors-headers 和 nginx 配置:预检响应缺少 CORS 标头

Posted

技术标签:

【中文标题】django-cors-headers 和 nginx 配置:预检响应缺少 CORS 标头【英文标题】:django-cors-headers and nginx config: preflight response missing CORS headers 【发布时间】:2020-02-21 19:45:08 【问题描述】:

我使用 django-cors-headers 3.1.1 来处理我的 Django 后端和 javascript 前端应用程序之间的请求和响应。传输是不安全的(即 http,不是 https)。

在本地托管时,一切正常。但是在服务器上部署后,我不再看到 CORS 标头了。

以下是开发中的标头: 并在生产中:

错误信息:

Access to XMLHttpRequest at 'http://[HOST_IP]/api/assets/' from origin 'http://my_custom_domain.eu' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

我的 nginx 配置如下:

server 
    listen 80;
    server_name [HOST_IP];

    location / 
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/[path_to_app]/app.sock;

        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, PUT, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Max-Age' 86400;

        if ($request_method = 'OPTIONS') 
            add_header 'Content-Type' 'text/html; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        
        if ($request_method = 'PUT') 
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        
        if ($request_method = 'GET') 
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        
    

    location /static/ 
        autoindex on;
        alias /home/ubuntu/[path_to_app]/site/static/;
    

django-cors-headers 的设置现在在开发和生产中是相同的:

INSTALLED_APPS = (
    …
    "corsheaders",
    …
)
MIDDLEWARE = [
    …
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    …
]
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = ['DELETE','GET','OPTIONS','PATCH','POST','PUT']

在客户端,我尝试添加“Access-Control-Request-Method”:“PUT”标头,但被浏览器拒绝。客户通话中注意到异常:

  axios(
    method: 'put',
    url: `$this.backendUrl/api/assets/`,
    data: formData,
    headers: 
      'Content-Type': 'application/octet-stream',
    
  )

另外,我是第一次尝试在 Amazon AWS EC2 上托管,所以可能有一些我不知道的必需 AWS 配置。例如,是否需要使用 API 网关启用 CORS? documentation 没有这么说(‘如果您使用 API Gateway Import API,您可以使用 OpenAPI 文件设置 CORS 支持’)。

前端应用程序托管在具有以下 CORS 策略的 S3 存储桶上:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

我在这里缺少什么?是否需要一些服务器端(尤其是 nginx)配置?

我尝试过的其他一些解决方案:

我怀疑请求/响应来源是否正确(例如 APPEND_SLASH 变量)。但是如果是这样的话,本地托管不应该报错吗?

我也尝试过像this question那样设置代理头,但是在不太了解nginx的情况下这注定要失败。

【问题讨论】:

【参考方案1】:

我通过改变 3 件事设法解决了这个问题:

AWS

我注意到AWS documentation 声明:

已经为 Amazon EC2 API 启用了 CORS,并且为您准备好了 使用。您无需执行任何额外的配置步骤 开始使用此功能。你的方式没有改变 调用 Amazon EC2 API;他们仍然必须使用有效的签名 AWS 凭证以确保 AWS 可以对请求者进行身份验证。 […]

这通常由 AWS SDK 或 CLI 处理,但在我的情况下,我没有使用它们,所以我必须添加 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY。就我而言,我只是使用了aws4 library:

  axios(aws4.sign(
    accessKeyId: this.awsAccessKey,
    secretAccessKey: this.awsSecretAccessKey,
    method: 'put',
    url: `$this.backendUrl/api/assets/`,
    data: formData,
    body: JSON.stringify(formData),
    service: 'ec2',
    region: 'eu-central-1',
    path: '/',
    headers: 
      'Content-Type': 'application/octet-stream'
    
  ))

不过,我已经看到很多示例如何添加 AWS Signature v.4 而无需任何额外的依赖。

NGINX

在 nginx 配置中,我将所有 add_headers 语句放入条件代码块中。想法来自this post。

server 
    listen 80;
    server_name [HOST_IP];

    location / 
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/[path_to_app]/app.sock;


        if ($request_method = 'OPTIONS') 
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, PUT, OPTIONS, POST, DELETE';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-Amz-Date';
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Content-Type' 'text/html; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        
        if ($request_method = 'PUT') 
            add_header 'Access-Control-Allow-Methods' 'GET, PUT, OPTIONS, POST, DELETE';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-Amz-Date';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        
        if ($request_method = 'GET') 
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, PUT, OPTIONS, POST, DELETE';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-Amz-Date';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        
    

    location /static/ 
        autoindex on;
        alias /home/ubuntu/analizy/be/site/static/;
    

Django-cors-header

在这里添加非默认标题就足够了。

from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = list(default_headers) + [
    'X-Amz-Date',
]

希望这会对某人有所帮助。

【讨论】:

以上是关于django-cors-headers 和 nginx 配置:预检响应缺少 CORS 标头的主要内容,如果未能解决你的问题,请参考以下文章

Django-cors-headers解决跨域问题

Wagtail、CORS 和 Django-Cors-Headers。如何启用 CORS 以便 AXIOS 可以访问端点

我正在使用 angularjs 和 django-cors-headers 然后给出“这对于需要预检的跨域请求是不允许的。”

如何使用 django-cors-headers 让我的 API 正常工作而不会出现 405 错误

django-cors-headers

即使使用 django-cors-headers 也得到 304 响应