为啥 Flask-Cors 在生产中没有检测到我的跨域域?

Posted

技术标签:

【中文标题】为啥 Flask-Cors 在生产中没有检测到我的跨域域?【英文标题】:Why is Flask-Cors not detecting my Cross-Origin domain in production?为什么 Flask-Cors 在生产中没有检测到我的跨域域? 【发布时间】:2021-04-11 17:21:59 【问题描述】:

我的网站前后端是分开的,所以我的后端服务器需要开放CORS权限,这样前端才能向它请求数据。

我在开发中使用Flask-Cors成功,但是当我部署到生产环境时它不起作用。 (请注意,我已经查看了关于 SO 的其他烧瓶-cors 问题,但没有一个适合我的情况)

以下是正在工作开发的相关代码:

# 3rd party imports
import flask
from flask import Flask, request, redirect, send_from_directory, jsonify
from flask_cors import CORS

# Create the app
app = Flask(__name__)
CORS(app, origins=[
  'http://localhost:5001',
])

# Define the routes
@app.route('/')
def index():
  # no CORS code was necessary here
  app.logger.info(f'request is: flask.request')

我尝试过的:

将我的服务器的 IP 地址 'http://162.243.168.182:5001' 添加到 CORS 列表中不足以解决问题,尽管我知道它应该在那里。 似乎使用'*' 允许所有来源 工作。 (非常可疑!)

请注意,我使用的是 Docker 容器,因此我在开发和生产之间的环境几乎相同。但不同的是我在不同的服务器上,并且我修改了前端以将请求发送到新的 IP 地址(导致著名的“Access-Control-Allow-Origin” header missing CORS 错误)。

现在我想知道 flask.request 对象是否以某种方式丢失了信息,这导致 Flask-Cors 无法像预期的那样发送 Access-Control-Allow-Origin 标头。如果您认为有帮助,我可以提供该日志记录信息!

更多信息!

我在 PROD 中使用的 Dockerfile 是:

# base image
FROM tiangolo/uwsgi-nginx-flask:python3.8-2020-12-19

# install deps
RUN pip3 install ediblepickle==1.1.3
# RUN pip3 install flask==1.1.2 # pre-installed on tiangolo/uwsgi-nginx-flask
RUN pip3 install flask-cors==3.0.9
RUN pip3 install numpy==1.19.2
RUN pip3 install scipy==1.5.2
RUN pip3 install pandas==1.1.2
RUN pip3 install networkx==2.5

# pull in files for deployment
COPY ./app /app

# Note that there is no CMD to run because the CMD set in the base image is what we already wanted.  As long as the Flask app is called `app`, the python file is named `main.py`, the parent directory is named `app`, and that same directory gets copied into `/app`, then the base image is designed to make our app work out-of-the-box.

我用来启动它的命令是:

docker build -t mvlancellotti/tennis-backend:prod -f prod.Dockerfile . && docker run --rm -p 5000:80 --name tennis-backend-container mvlancellotti/tennis-backend:prod

进入容器的/app目录,有文件uwsgi.ini,内容为:

[uwsgi]
module = main
callable = app

这似乎有效,文件/etc/nginx/nginx.conf 有内容:

user  nginx;
worker_processes 1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events 
    worker_connections 1024;

http 
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;

daemon off;

文件/etc/nginx/conf.d/nginx.conf 有内容:

server 
    listen 80;
    location / 
        try_files $uri @app;
    
    location @app 
        include uwsgi_params;
        uwsgi_pass unix:///tmp/uwsgi.sock;
    
    location /static 
        alias /app/static;
    

【问题讨论】:

你的 nginx/apache 配置是什么样的? @CodeLikeBeaker 请参阅我现在在上面添加的nginx.conf @CodeLikeBeaker 我现在在上面添加了第二个nginx.conf 文件。我不知道为什么有两个。 【参考方案1】:

问题是,请求资源的服务器应该有 CORS 标头(让它是 serverA),而不是需要远程的服务器文件 (serverB)。如果serverANGINXApache2,您可以使用它在响应中添加CORS 标头:

NGINX

add_header Access-Control-Allow-Origin *;
# or
add_header Access-Control-Allow-Origin serverB;

Apache2

Header set Access-Control-Allow-Origin "*"
# or
Header set Access-Control-Allow-Origin "serverB"

上面的意思是“允许*或serverB从这个服务器(serverA)获取资源”

对于刚接触 CORS 的任何人,我推荐这两个(1、2)来自 Mozilla 的简短读物。我认为他们很擅长解释基础知识。

【讨论】:

我已将add_header Access-Control-Allow-Origin *; 添加到我的/etc/nginx/conf.d/nginx.conf 文件中,但同样的CORS 错误仍然存​​在:跨源请求被阻止:同源策略不允许读取localhost:5000/… 的远程资源. (原因:CORS 请求未成功)。 请查看我在上面提供的额外信息。 @mareoraft 你把那条线放在哪里了?从配置来看,您应该将其放在location @applocation / 中。或者两者兼而有之,如果初始请求可能在两个地方结束。还记得在进行更改后重新启动或重新加载 NGINX。 我已将该行放入location / 块中,现在我已将其放入两者中,但错误仍然存​​在。我实际上是在修改文件之前使用exec /usr/bin/supervisord 启动服务器,这显然启动了Supervisor,“使用Nginx 和uWSGI”。 为什么我们确信 nginx 是这里唯一的问题?我们可以采取哪些日志记录/故障排除步骤来查看 nginx 是否正在接收并阻止请求?浏览器给出的错误消息是localhost而不是162.243.168.182,这非常可疑。让我看看前端是否使用了正确的 url...【参考方案2】:

请注意,您需要在生产环境中考虑几件事情。 首先,请分享您与 docker 相关的配置(如果您有 Dockerfile、Docker-Compose),因为您正在尝试在与根本原因无关的地方解决问题。例如,如果您使用 ApacheNginx 将 HTTP 请求作为 reverse_proxy 提供服务,那么您需要将相关标头添加到它们的配置中。但我建议你使用下面的代码,以前我遇到过像你这样的问题,下面的代码解决了我的问题:


cors = CORS(flask_app, resources=r"/api/*": "origins": "*", "allow_headers": "*", "expose_headers": "*")

更新:添加 Nginx 配置

这是 Nginx 最完整和最广泛的 CORS 配置:

Nginx 配置完成 CORS 打开

对于某些配置,某些旧浏览器不支持通配符(* 符号),因此,我添加了一个包含所有可能值的列表,但您需要根据您的应用程序要求和策略对其进行修改。此外,您可以将“add_headers”部分移动到配置文件中需要的任何位置。

server 
    listen 80;

    
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'CONNECT, DEBUG, DELETE, DONE, GET, HEAD, HTTP, HTTP/0.9, HTTP/1.0, HTTP/1.1, HTTP/2, OPTIONS, ORIGIN, ORIGINS, PATCH, POST, PUT, QUIC, REST, SESSION, SHOULD, SPDY, TRACE, TRACK';
    add_header 'Access-Control-Allow-Headers' 'Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With';
    add_header 'Access-Control-Expose-Headers' 'Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With';


    location / 
        try_files $uri @app;
    
    location @app 
        include uwsgi_params;
        uwsgi_pass unix:///tmp/uwsgi.sock;
    
    location /static 
        alias /app/static;
    


Nginx 更简单的 CORS 配置文件

此外,您可以使用以下代码,但可能会遇到一些 cors 错误,您可以通过与之前的示例进行比较来快速解决这些错误。大多数情况下,响应中有一些标题在列表中是allowedexposed

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, HEAD';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Origin,Content-Type,Accept,Authorization,Access-Control-Expose-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Expose-Headers';
add_header 'Access-Control-Expose-Headers' 'Origin,Content-Type,Accept,Authorization,Access-Control-Expose-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Expose-Headers'

您还可以查看此链接以了解有关 CORS 的更多详细信息。 Link

【讨论】:

我使用了您建议的行(将/api/修改为适当的路径),但我仍然收到CORS错误。 请查看我添加到问题中的其他信息。 @mareoraft,使用 /api/v1/ 进行 API 的版本管理,如果您为某些移动应用程序提供 API,这将很有用。您可以在 /v2/ 上发布新的主要 API,并保持 /v1/ 上的旧 API 向后兼容。但重点是其他参数,例如在我的 python 代码中公开标头和 ..,请检查它们。 谢谢。我明天去看看。我的问题尚未解决,但我正在接近。 再次感谢,祝您有美好的一天。【参考方案3】:

我希望添加评论,但仍然没有足够的代表..

Nginx add_header 对错误代码不起作用。

此外,当您收到任何类型的错误(400、500、502 等)时,标头也会丢失。浏览器会显示 CORS,但没关系,其他地方出了问题。 因此浪费很多时间是很常见的......

我访问了您的应用(抱歉,如果未提及这样做)。它加载并且一些过滤器选项导致 502 并且浏览器会说:哦,CORS! 但看起来有什么东西正在消亡然后又回来了。

关于 add_header 和错误的信息:https://serverfault.com/questions/431274/nginx-services-fails-for-cross-domain-requests-if-the-service-returns-error

【讨论】:

从您提到的链接add_header 'Access-Control-Allow-Origin' '*' always; 克服了错误代码的问题。 PS:有一些我的代表:)

以上是关于为啥 Flask-Cors 在生产中没有检测到我的跨域域?的主要内容,如果未能解决你的问题,请参考以下文章

laravel 或 AWS 没有检测到我的 https

为啥我在尝试设置 PayPal 付款时在生产中不断收到 INVALID_RESOURCE_ID 错误?

websocket-rails 在生产中没有连接

BOM在生产管理中的作用

Rabbitmq celeryd celerybeat 没有在生产中作为守护进程执行任务

为啥服务器没有检测到我的 python 项目中的更改源代码?