CORS - 在预检请求之后发出的请求期望'Access-Control-Allow-Credentials'为真

时间:2015-06-11 11:33:36

标签: javascript jquery ajax zend-framework cors

与往常一样,我会尽可能地将问题变得干净利落。

我必须发送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”,但“Access-Control-Allow-Credentials”   标题是''。允许凭证必须是“真实的”。

第二个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解决了该问题:   对于服务器返回的所有响应(PUT,GET,POST,DELETE,OPTIONS)

3 个答案:

答案 0 :(得分:4)

根据W3C CORS docs§7.1.5第3步,预检和实际请求都必须执行资源共享检查"。从定义该检查的§7.2开始:

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

    [..]

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

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

答案 1 :(得分:0)

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

我无法确定,但我认为浏览器的行为也不同。例如,Chrome现在始终会发送Origin标题,而不仅仅是在发布前请求。

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

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

答案 2 :(得分:0)

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

有关参数

的更多信息
options.xhrFields = {
        withCredentials: true
 };

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

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

希望它有所帮助。