Cross-origin redirection denied by Cross-Origin Resource Sharing policy in Safari
我们有一个API端点可以重定向到另一个服务器。它是通过XHR调用的,在大多数浏览器中似乎都可以正常工作,除了Safari(特别是iOS)。
我进入控制台的错误是:跨源资源共享策略拒绝跨源重定向
我们在执行重定向的页面和其他服务器上有CORS。重定向页设置:
1 2 | Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: false |
另一台服务器具有:
1 | Access-Control-Allow-Origin: * |
如何在CORS策略中允许重定向?
W3C规范和其他权威来源直接禁止通配符
Note: The string"*" cannot be used for a resource that supports credentials.
https://www.w3.org/tr/cors/资源请求
Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding.
https://developer.mozilla.org/en-us/docs/web/http/access-control-cors-requests-with-u-credentials
If credentials mode is"include", then
Access-Control-Allow-Origin cannot be* .
https://fetch.spec.whatwg.org/CORS协议和凭证
进一步步骤因为你的问题缺乏细节,我们来做一些定义:
- 域A是客户端代码的域
- DOMAIN-B是向其发出请求的端点所在的域
- 域C是请求最终重定向到的域
首先,我认为,你想做一个变通方案。只要你告诉所有的终点都在你的控制之下,那么你要么:
- 从域A直接向域C发出请求(或者甚至发出条件请求,如果重定向取决于参数)
- 公开后端中将请求包装到域B的另一个端点
如果一个bug真的违反了规范,那么向Webkit追踪器报告它也是很重要的。为了更容易地重现这个案例,我制作了一个Cherrypy应用程序,您可以将它附加到报告中。运行步骤:
在终端中运行这些命令
1 2 3 4 | virtualenv -p python3 venv . venv/bin/activate pip install cherrypy python corsredirect.py |
将浏览器指向http://domain-a:8080并按下按钮
这是应用程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #!/usr/bin/env python3 ''' Add localhost aliases in /etc/hosts for"domain-a","domain-b","domain-c". The flow is: [domain-a] --CORS-GET--> [domain-b] --redirect--> [domain-c]. Open as http://domain-a:8080/ ''' import cherrypy def cors(): cherrypy.response.headers['Access-Control-Allow-Origin'] = '*' cherrypy.tools.cors = cherrypy._cptools.HandlerTool(cors) class App: @cherrypy.expose def index(self): return '''<!DOCTYPE html> <html> <head> <meta content='text/html; charset=utf-8' http-equiv='content-type'> CORS redirect test </head> <body> <button>make request</button> <script type='text/javascript'> document.querySelector('button').onclick = function() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain-b:8080/redirect', true); xhr.onload = function() { var text = xhr.responseText; console.log('success', text); }; xhr.onerror = function() { console.error('failure'); }; xhr.send(); }; </body> </html> ''' @cherrypy.expose @cherrypy.config(**{'tools.cors.on': True}) def redirect(self): raise cherrypy.HTTPRedirect('http://domain-c:8080/endpoint') @cherrypy.expose @cherrypy.config(**{'tools.cors.on': True}) @cherrypy.tools.json_out() def endpoint(self): return {'answer': 42} if __name__ == '__main__': config = { 'global' : { 'server.socket_host' : '127.0.0.1', 'server.socket_port' : 8080, 'server.thread_pool' : 8 } } cherrypy.quickstart(App(), '/', config) |
你试过了吗?
1 2 3 | Access-Control-Allow-Origin: * Access-Control-Allow-Headers:"Origin, X-Requested-With, Content-Type, Accept" Access-Control-Allow-Methods: * |
欲了解更多信息,请访问:http://enable-cors.org/
如果您可以更改您的URL,您可以在Apache上尝试使用代理传递配置。呼叫看起来像在同一个域中,但不会。https://httpd.apache.org/docs/current/fr/mod/mod_proxy.html
我在Heroku应用程序上托管API时遇到了类似的问题。在chrome和firefox上,来自另一个域对我的API的请求工作得很好,但在Safari上,我遇到了一个令人沮丧的"跨源重定向被跨源资源共享策略拒绝"。
经过一些研究,似乎Safari中有一个bug阻止了一些CORS重定向。我可以通过直接请求Heroku应用程序(
如果您有一个重定向到API的设置,直接请求根域可能会有所帮助。
您需要设置"访问控制允许方法"头。
1 | Access-Control-Allow-Methods: * |
在两个服务器上启用HTTPS已经为我解决了问题。
对于除"简单请求"以外的任何内容,权威源拒绝"访问控制允许来源"头的通配符,并要求您显式设置头"访问控制允许来源"。
下面是Mozilla对"简单请求"的声明:
The only allowed methods are:
- GET
- HEAD
- POST
Apart from the headers set automatically by the user agent (e.g.
Connection, User-Agent, etc.), the only headers which are allowed to
be manually set are:
- Accept
- Accept-Language
- Content-Language
- Content-Type
The only allowed values for the Content-Type header are:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
当您的请求不满足"简单请求"的要求时,您可能会陷入"预先点燃的请求"。
methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
对于认证申请-
Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. Access-Control-Allow-Origin: *
由于您没有提供来自您的页面和服务器的实际HTTP请求和响应,因此我必须做出一些假设。我假设您的页面是在foo.example域下加载的,并且您的api是在同一foo.example域下的,而您的"其他"服务器是在域栏上的。
您可能需要设置页面,以便使用这些头向"其他"服务器发出重定向请求:
1 2 | Access-Control-Request-Method: GET, OPTIONS Access-Control-Request-Headers: x-requested-with, Content-Type, CUSTOM-HEADER |
然后,您可能需要设置"其他"服务器以响应选项请求:
1 2 3 | Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Methods: GET, OPTIONS Access-Control-Allow-Headers: x-requested-with, Content-Type, CUSTOM-HEADER |
那么您的页面应该能够完成请求。