Safari 中的跨域资源共享策略拒绝了跨域重定向

Posted

技术标签:

【中文标题】Safari 中的跨域资源共享策略拒绝了跨域重定向【英文标题】:Cross-origin redirection denied by Cross-Origin Resource Sharing policy in Safari 【发布时间】:2017-04-04 08:00:13 【问题描述】:

我们有一个重定向到另一个服务器的 api 端点。它通过 XHR 调用,似乎在大多数浏览器中都能正常工作,除了 Safari(特别是在 ios 上)。

我在控制台中遇到的错误是: 跨域资源共享策略拒绝跨域重定向

我们在执行重定向的页面和另一台服务器上都有 CORS。重定向页面设置:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: false

另一台服务器有:

Access-Control-Allow-Origin: *

如何在 CORS 策略中允许重定向?

【问题讨论】:

如果跨域 XHR 在其他浏览器中工作但在 Safari 中不起作用,这听起来像是 Safari 中的一个错误,使其不符合fetch.spec.whatwg.org 中指定的当前 CORS 要求。所以请考虑在bugs.webkit.org 提交错误 这可能是 Safari 的一个错误,但我希望有一个解决方法。 查看***.com/questions/5750696/… 谢谢,但似乎没有提到任何工作。 请参考此链接***.com/questions/16824661/…。希望能帮助到你。我在 ASP .net 网站上遇到了同样的问题。我们当时无法解决。 【参考方案1】:

W3C 规范和其他权威来源直接禁止通配符Access-Control-Allow-OriginAccess-Control-Allow-Credentials: true 一起使用。

注意:字符串“*”不能用于支持凭据的资源。

https://www.w3.org/TR/cors/#resource-requests

重要提示:在响应凭证请求时,服务器必须指定域,并且不能使用通配符。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials

如果凭据模式为“包含”,则 Access-Control-Allow-Origin 不能为 *

https://fetch.spec.whatwg.org/#cors-protocol-and-credentials

进一步的步骤

由于您的问题缺乏细节,我们来做一些定义:

domain-a 是您的客户端代码的域 domain-b 是您发出请求的端点的域 domain-c 是请求最终重定向到的域

首先,我认为,您想制定一个解决方法。只要您告诉所有端点都在您的控制之下,那么您可以:

domain-adomain-c 发出直接请求(如果重定向取决于参数,甚至发出条件请求) 在您的后端公开另一个端点,该端点将请求包装到 domain-b

如果确实违反规范,向WebKit tracker 报告错误也很重要。为了更容易重现这个案例,我制作了一个 CherryPy 应用程序,您可以将其附加到报告中。运行步骤:

    将“127.0.0.1 domain-a domain-b domain-c”添加到您的/etc/hosts 将以下代码放入corsredirect.py

    在终端中运行这些命令

     virtualenv -p python3 venv
     . venv/bin/activate
     pip install cherrypy
     python corsredirect.py
    

    将浏览器指向http://domain-a:8080并按下按钮

有应用程序。

#!/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'>
      <title>CORS redirect test</title>
      </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();
          ;
        </script>
      </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)

【讨论】:

我也用Access-Control-Allow-Credentials: false 尝试过,但它不能解决这个问题。在我的生产代码中,我已经将此设置为 false。 @Noodles 明确指出 Access-Control-Allow-Credentialsy value is not the problem, tou might want to consider updating the example code in the question to use Access-Control-Allow-Credentials: false` 并且不使用 Access-Control-Allow-Credentials: true。因为它是一个错误。 @Noodles 我已经更新了我的答案。您能否验证 Safari 是否在应用程序中失败了请求?【参考方案2】:

在两台服务器上启用 HTTPS 为我解决了这个问题。

【讨论】:

【参考方案3】:

我在 Heroku 应用上托管我的 API 时遇到了类似的问题。在 Chrome 和 Firefox 上,来自另一个域的对我的 API 的请求运行良好,但在 Safari 上,我得到了令人沮丧的“跨域资源共享策略拒绝了跨域重定向”。

经过一些研究,Safari 中似乎有一个 bug 可以防止使用 CORS 进行一些重定向。我可以通过直接请求 Heroku 应用程序 (myapp.herokuapp.com/api) 而不是我的域 (mydomain.com/api) 来解决这个问题。

如果您的设置中有指向 API 的重定向,则直接请求根域可能会有所帮助。

【讨论】:

【参考方案4】:

如果您可以更改您的网址,您可以尝试在您的 Apache 上使用代理通行证配置。呼叫看起来像在同一个域上,但它不会。 https://httpd.apache.org/docs/current/fr/mod/mod_proxy.html

【讨论】:

【参考方案5】:

对于“简单请求”以外的任何内容,权威来源拒绝“Access-Control-Allow-Origin”标头的通配符,并要求您明确设置标头“Access-Control-Allow-Headers”。

这是Mozilla states for "Simple request":

唯一允许的方法是:

获取 头 发布

除了由用户代理自动设置的标题(例如 Connection、User-Agent 等),唯一允许的标头 需要手动设置的是:

接受 接受语言 内容-语言 内容类型

Content-Type 标头的唯一允许值是:

application/x-www-form-urlencoded 多部分/表单数据 文本/纯文本

当您的请求不符合“简单请求”要求时,您可能属于"Preflighted Requests"。

GET、HEAD 或 POST 以外的方法。此外,如果 POST 用于发送 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 的请求数据,例如如果 POST 请求使用 application/xml 或 text/xml 将 XML 有效负载发送到服务器,则请求被预检。

关于凭证请求 -

重要提示:在响应凭据请求时,服务器必须指定域,并且不能使用通配符。访问控制允许来源:*

由于您没有提供来自您的页面和服务器的实际 HTTP 请求和响应,我将不得不做出一些假设。我假设您的页面在域 foo.example 下加载,并且您的 API 在同一个 foo.example 域下,并且您的“其他”服务器在域 bar.example 上。

您可能需要设置您的页面以使用这些标头向“其他”服务器发出重定向请求:

Access-Control-Request-Method: GET, OPTIONS
Access-Control-Request-Headers: x-requested-with, Content-Type, CUSTOM-HEADER

然后您可能需要设置您的“其他”服务器以响应选项请求:

Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: x-requested-with, Content-Type, CUSTOM-HEADER

那么你的页面应该能够完成请求。

【讨论】:

【参考方案6】:

你试过了吗?

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: *

更多检查:http://enable-cors.org/

【讨论】:

【参考方案7】:

您需要设置“Access-Control-Allow-Methods”标头。

Access-Control-Allow-Methods: *

【讨论】:

很遗憾,这并没有解决问题

以上是关于Safari 中的跨域资源共享策略拒绝了跨域重定向的主要内容,如果未能解决你的问题,请参考以下文章

前端跨域问题

跨域:跨域及解决方法

PHP解决mui中ajax的跨域问题

SSO单点登录跨域重定向跨域设置Cookie京东单点登录实例分析

前端常见的跨域解决方案

项目中经常遇到的跨域请求的几种方法