Python Flask CORS - API总是允许任何来源

4

我已经查看了许多SO答案,但似乎找不到这个问题。我感觉我可能只是错过了一些显而易见的东西。

我有一个基本的Flask API,并实现了flask_cors扩展和自定义的Flask修饰器 [@crossdomain from Armin Ronacher]1 (http://flask.pocoo.org/snippets/56/) ,两者都显示相同的问题。

这是我的示例应用程序:

application = Flask(__name__,
            static_url_path='',
            static_folder='static')
CORS(application)
application.config['CORS_HEADERS'] = 'Content-Type'

@application.route('/api/v1.0/example')
@cross_origin(origins=['http://example.com'])
# @crossdomain(origin='http://example.com')
def api_example():
  print(request.headers)
  response = jsonify({'key': 'value'})
  print(response.headers)
  return response

(已插入EDIT 3):

当我从浏览器中的JS向该端点发出GET请求时(来自127.0.0.1),它总是返回200,而我希望看到的是:

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:5000' is therefore not allowed access. The response had HTTP status code 403.

CURL:

ACCT:ENVIRON user$ curl -i http://127.0.0.1:5000/api/v1.0/example
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 20
Access-Control-Allow-Origin: http://example.com
Server: Werkzeug/0.11.4 Python/2.7.11
Date: [datetime]

{
  "key": "value"
}

日志:

Content-Length: 
User-Agent: curl/7.54.0
Host: 127.0.0.1:5000
Accept: */*
Content-Type: 


Content-Type: application/json
Content-Length: 20


127.0.0.1 - - [datetime] "GET /api/v1.0/example HTTP/1.1" 200 -

我甚至没有看到响应中的所有正确标头,并且似乎不关心请求的来源。有什么想法吗?谢谢!
编辑:
另外,查看文档示例这里https://flask-cors.readthedocs.io/en/v1.7.4/#a-more-complicated-example),它显示:
@app.route("/")
def helloWorld():
    '''
        Since the path '/' does not match the regular expression r'/api/*',
        this route does not have CORS headers set.
    '''
    return '''This view is not exposed over CORS.'''

...这很有趣,因为我已经暴露了根路径(和其他路径),没有任何CORS装饰,而且它们从任何来源都可以正常工作。所以似乎这个设置存在根本性的问题。

在这方面,教程(此处)https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask)似乎表明Flask API应该自然地暴露而不受保护(我认为这只是因为CORS扩展还没有被应用),但我的应用程序基本上就像CORS扩展甚至不存在一样运行(除了日志中的一些注释,您可以看到)。


编辑2:

我的评论不太清楚,因此我在AWS API Gateway上创建了三个具有不同CORS设置的示例端点。它们是简单返回“success”的GET方法端点:

1)未启用CORS(默认):

响应:

XMLHttpRequest无法加载 https://t9is0yupn4.execute-api.us-east-1.amazonaws.com/prod/cors-default。 Preflight请求的响应未通过访问控制检查:所请求的资源上没有'Access-Control-Allow-Origin'头。因此,源'http://127.0.0.1:5000'不允许访问。响应的HTTP状态代码为403。

2)启用CORS-来源受限:

响应:

XMLHttpRequest无法加载https://t9is0yupn4.execute-api.us-east-1.amazonaws.com/prod/cors-enabled-example。 预检请求的响应未能通过访问控制检查:'Access-Control-Allow-Origin'标头的值为http://example.com,而不是提供的来源'http://127.0.0.1:5000'。因此,不允许访问该来源。
3)启用CORS - 来源通配符: 响应:
"success"

我对基础设施不是很有经验,但我期望启用Flask CORS扩展程序后,我的api端点会根据我在origins=设置中设置的内容而模仿这种行为。在这个Flask设置中,我漏掉了什么?
解决方案编辑:
好吧,鉴于我的某些地方显然不正常,我精简了我的应用程序,并为每个CORS来源限制的变化重新实现了一些非常基本的API。我一直在使用AWS弹性Beanstalk来托管测试环境,所以我重新上传了那些示例并运行了一个JS ajax请求。它现在可以工作。
我在裸露的端点上遇到了Access-Control-Allow-Origin错误。似乎当我为部署配置应用程序时取消注释CORS(application, resources=r'/api/*')时,这显然允许所有起源访问裸露的端点!
我不确定为什么我的具有特定限制(origins=[])的路由也允许一切,但那一定是一些小错误或笔误之类的东西,因为现在它正在工作。
特别感谢sideshowbarker提供的帮助!

抱歉,我仍然不清楚您的期望是什么。在编辑2中,所有这些情况看起来都像是按预期工作的。第1种情况也是您认为按预期工作的情况,对吗?在那种情况下,您没有为该端点启用CORS,因此浏览器不允许访问响应。就CORS协议而言,第2种情况也按预期工作,因为您已将允许的来源设置为“http://example.com”,但您正在从“http://127.0.0.1:5000”发送请求,因此浏览器不会让您在“http://127.0.0.1:5000”上运行的代码看到响应。 - sideshowbarker
第三个案例在CORS协议方面也如预期工作——因为根据您所说的配置,“允许运行在任何来源的前端代码访问此端点的响应”,因此从您发送请求的来源运行的代码成功地访问了响应。 - sideshowbarker
我不理解“cause my api endpoints to mimic this behavior depending on what I set at the origins= setting”这句话的意思。 - sideshowbarker
我的Flask端点的工作方式类似于Case#3,无论是否使用CORS扩展,或者我在装饰器中包含“origins =”什么。它们从未像Case#1和#2那样给我响应。我期望Flask应用程序像这些示例一样响应,这是我的错误吗? - sean
我应该澄清一下,因为我认为我的问题中发生了两件不同的事情,这让问题变得混乱了。Sideshowbarker,你是正确的,服务器不会阻止任何东西,正如curl响应所示。那是我的误解。然而,我在浏览器中用JS运行了相同的端点,即使我原来的问题很困惑,它也不像Case#1(如果我不使用CORS)或Case#2(如果我使用装饰器:"@cross_origin(origins=['http://example.com'])")。所以即使我的原始问题令人困惑,它仍然表现得很奇怪。我会编辑那个curl部分,因为那是一个糟糕的例子。 - sean
显示剩余3条评论
1个回答

5

根据您的问题,我们不完全清楚您期望的行为。但就CORS协议的工作方式而言,您的服务器似乎已经按预期运行。

具体而言,问题中引用的curl响应显示了此响应头:

Access-Control-Allow-Origin: http://example.com

这表示服务器已经配置为告诉浏览器:“只有当代码运行在源 http://example.com 时,才允许来自前端 JavaScript 代码在浏览器中的跨域请求。”
如果您期望的行为是服务器现在将拒绝来自非浏览器客户端(例如curl)的请求,则仅靠CORS配置不足以使服务器这样做。
当您使用CORS支持对服务器进行配置时,服务器唯一不同的操作就是发送Access-Control-Allow-Origin响应头和其他CORS响应头。就是这样。
实际执行CORS限制的是浏览器,而不是服务器。
因此,无论您进行何种服务器端CORS配置,服务器仍然会接受来自所有客户端和源的请求;换句话说,所有客户端从所有源都会像以前一样从服务器获取响应。
但是,仅当请求的服务器通过响应允许该来源的Access-Control-Allow-Origin头来允许请求时,浏览器才会向特定来源的前端JavaScript代码公开跨域请求的响应。 这是您可以使用CORS配置完成的唯一操作。您不能仅通过进行任何服务器端CORS配置来使服务器仅接受和响应来自特定来源的请求。要做到这一点,您需要使用除CORS配置之外的其他东西。

谢谢您对此的帮助。也许我可以举个例子来解释一下。我在AWS上设置了一个API Gateway端点,并尝试在未启用CORS的情况下访问它。它返回以下响应:“请求的资源上没有'Access-Control-Allow-Origin'标头。因此,不允许从'http://another-example.com'访问。” 我基本上想让我的Flask应用程序在未启用应用程序中的CORS时发送该响应,但它总是允许任何访问,甚至是来自浏览器中js的ajax请求。我以为我理解CORS,但也许缺少服务器设置? - sean
就「试图让我的Flask应用在未启用CORS的情况下发送响应,但即使来自浏览器中JS的ajax请求也始终允许任意访问」而言,问题中添加的示例并未表明「它始终允许任意访问,即使来自浏览器中JS的ajax请求」- 相反,它们表明浏览器按照服务器发送的Access-Control-Allow-Origin头所期望的方式阻止对响应的访问。您是否仍然期望服务器本身进行某种阻塞?服务器本身从不进行任何阻塞。 - sideshowbarker
EDIT 2的示例不是来自我的Flask应用程序。它们是在AWS API Gateway上的示例端点。是的,它们是我所期望发生的,但我的Flask应用程序没有以同样的方式运行。 - sean
在原问题和评论中添加了澄清。我通过引入curl示例使这个问题变得混乱了。 - sean
你可能需要更新你的问题,包括你在前端JavaScript代码中实际获取的确切响应。我的意思是,在浏览器开发工具的网络面板中检查响应,并将响应头复制并粘贴到你的问题中。如果你正在进行的请求实际上是跨域的——而不是从发送请求的服务器相同的源——那么我认为你会发现,服务器正在发送一个带有“*”通配符的Access-Control-Allow-Origin响应头。 - sideshowbarker
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接