【问题标题】:CORS - request made after preflight request expects 'Access-Control-Allow-Credentials' to be trueCORS - 在预检请求后发出的请求期望“访问控制允许凭据”为真
【发布时间】:2015-08-27 01:55:21
【问题描述】:

与往常一样,我会尽量让我的问题甜美干净。

我必须将 CORS 作为需求请求发送,我正在通过 $.ajax 发送它,它看起来像这样:

$.ajaxSetup({
        beforeSend: function (jqXHR, options) {
            options.url = Utils.setUrlHostname(options.url);
            options.xhrFields = {
                withCredentials: true
            };
            jqXHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        }
    });

在服务器端(php - zend 框架)我正在处理这样的标头:

public function OPTIONS_ProxyAction()
    {
        $this->getResponse()->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, PUT, DELETE');
        $this->getResponse()->setHeader('Access-Control-Allow-Headers', 'Content-Type, x-requested-with');
        $this->getResponse()->setHeader('Access-Control-Allow-Credentials', 'true');
        $this->getResponse()->setBody(null);
        $this->_helper->viewRenderer->setNoRender(true);
    }

预检请求标头看起来不错,我得到了 200 OK:

请求标头:

Host: domain1111.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: https://domain2222.com
Access-Control-Request-Method: GET (this is seen as OPTIONS in network tab)
Access-Control-Request-Headers: x-requested-with
Connection: keep-alive
Cache-Control: max-age=0

和响应标头:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Content-Type
Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE
Access-Control-Allow-Origin: https://domain2222.com
Cache-Control: max-age=0, s-maxage=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: Keep-Alive
Content-Length: 0
Content-Type: text/html
Date: Thu, 11 Jun 2015 08:40:42 GMT
Etag: "d41d8cd98f00b204e9800998ecf8427e"
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Keep-Alive: timeout=5, max=100
Pragma: no-cache
Server: Apache
Vary: X-CDN
X-Frame-Options: SAMEORIGIN
X-Ua-Compatible: IE=edge,chrome=1

我得到空响应,这很好,新请求被发送到同一个地址,但方法是 GET 而不是 OPTIONS,它看起来像这样:

请求标头:

Host: domain1111.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: https://domain2222.com/some/request
Origin: https://domain1111.com
Cookie: s1=533C36A9095C003A; 
BGUID=some complicated guid here
Connection: keep-alive
Cache-Control: max-age=0

然后回应:

Access-Control-Allow-Origin: https://domain2222.com
Cache-Control: max-age=0, s-maxage=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: Keep-Alive
Content-Length: 3278
Content-Type: application/json
Date: Thu, 11 Jun 2015 08:40:43 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Keep-Alive: timeout=5, max=99
Pragma: no-cache
Server: Apache
Vary: X-CDN
X-Frame-Options: SAMEORIGIN

问题是在发送真正的请求后,我进入了 Firefox:

跨域请求被阻止:同源策略不允许读取 https://domain1111.com/some/request 的远程资源。 (原因: 在 CORS 标头“Access-Control-Allow-Credentials”中预期为“true”)

在 Chrome 中也是如此:

XMLHttpRequest 无法加载 https://domain1111.com/some/request。 凭据标志为“真”,但“访问控制允许凭据” 标题是''。必须为“true”才能允许凭据。

当第二个 CORS 请求完成时会发生这些错误。 所以在发送第一个预检请求后,它返回 200 OK 和一个空响应,这很好;发送第二个请求后,我收到了上面列出的错误,没有返回数据。

此外,Firefox 中的“响应”选项卡给了我这样的信息:

SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 on the JSON data

我不确定在第二次请求期间是否也发送了属性“withCredential”,并且服务器可能没有返回:

'Access-Control-Allow-Credentials', 'true'

为什么它只发生在 OPTIONS 请求中?

无论请求方法是什么,我都应该从服务器返回这些标头吗?或者也许ajaxSetup有问题?也许它不应该在第二个 CORS 请求中发送“withCredentials:true”?

更新:

已通过添加 Access-Control-Allow-Credentials 解决了该问题: true,对服务器返回的所有响应(PUT、GET、POST、DELETE、OPTIONS)

【问题讨论】:

    标签: javascript jquery ajax zend-framework cors


    【解决方案1】:

    根据W3C CORS docs§7.1.5步骤3,预检和实际请求都必须执行“资源共享检查”。来自定义检查的§7.2

    1. 如果响应包含零个或多个Access-Control-Allow-Origin 标头值,则返回失败并终止此算法。

    [..]

    1. 如果未设置 omit credentials 标志并且响应包含零个或多个 Access-Control-Allow-Credentials 标头值,则返回失败并终止此算法。

    换句话说,预检和实际响应都必须包含Access-Control-Allow-Origin 标头,如果使用,还必须包含Access-Control-Allow-Credentials 标头。

    【讨论】:

      【解决方案2】:

      我认为您需要为所有请求(飞行前和正常)返回 CORS 政策。我也认为这种行为改变了。我记得只针对飞行前请求实施了 CORS 政策。

      我不能确定,但​​我认为浏览器的行为也有所不同。例如,Chrome 现在总是发送 Origin 标头,而不仅仅是在飞行前请求中。

      我当前的实现(仅)查找 Origin 标头,如果需要 CORS 策略(Origin != domain)返回它。

      我还没有找到任何关于如何正确实施 CORS 的明确指南或标准文档。

      【讨论】:

      • 所以我应该设置 $this->getResponse()->setHeader('Access-Control-Allow-Credentials', 'true');服务器返回的所有请求?我不明白你回答的第二部分,你能举个例子吗?
      • 当请求中包含Origin 标头并且源域与您当前的域不同时发送标头。
      • 所以我已将 'Access-Control-Allow-Credentials', 'true' 添加到所有请求中,Chrome 工作正常,但 Firefox 仍在返回:“SyntaxError:JSON.parse:JSON 数据的第 1 行第 1 列的数据意外结束”
      • 查看网络选项卡(Firebug)中的请求。这应该会给你更多关于正在发生的事情的信息。
      【解决方案3】:

      仅为您的 OPTIONS 请求返回标头。 OPTIONS_ProxyAction() 发送参数withCredentials: true 时,需要为所有CORS 请求(GET、POST...)维护此标头Access-Control-Allow-Credentials: true

      有关参数的更多信息

      options.xhrFields = {
              withCredentials: true
       };
      

      和标头Access-Control-Allow-Credentials看这篇文章:

      http://www.ozkary.com/2015/12/api-oauth-token-access-control-allow-credentials.html

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 2018-10-31
        • 2019-10-28
        • 2012-02-24
        • 2019-03-22
        • 2019-12-05
        • 2019-10-31
        • 2014-12-20
        • 2019-09-09
        • 2019-01-04
        相关资源
        最近更新 更多