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

django-cors-headers and nginx config: preflight response missing CORS headers

我使用 django-cors-headers 3.1.1 来处理我的 Django-backend 和 Javascript-frontend 应用程序之间的请求和响应。传输是 non-secured(即 http,而不是 https)。

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

以下 header 正在开发中: 并在生产中:

错误信息:

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'header,但是被浏览器拒绝了。客户端调用有异常:

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

另外,我第一次尝试在 Amazon AWS EC2 上托管,所以可能有一些我不知道的必需 AWS 配置。例如,是否需要使用 API 网关启用 CORS? documentation 没有这么说('如果您使用 API 网关导入 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>

我在这里错过了什么?是否需要一些 server-side(尤其是 nginx)配置?

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

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

我也试过像 那样设置代理 headers,但由于对 nginx 不是很了解,这注定要失败。

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

AWS

我注意到 AWS documentation 指出:

CORS is already enabled for the Amazon EC2 API, and is ready for you to use. You do not need to perform any additional configuration steps to start using this feature. There is no change to the way that you make calls to the Amazon EC2 API; they must still be signed with valid AWS credentials to ensure that AWS can authenticate the requestor. […]

这通常由 AWS SDK 或 CLI 处理,但在我的例子中,我使用了其中的 none,因此我必须添加 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 语句放入条件 code-blocks 中。创意来自 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

这里只要加上non-defaultheaders.

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

希望这对某人有所帮助。