为什么浏览器在使用XMLHTTPRequest和CORS时不遵循重定向?

13

我正在使用RESTful API编写某项服务的Web应用程序。该API可在https://api.example找到,应用程序可在https://app.example找到。在Chrome和Firefox中,使用CORS的简单GET请求完全正常。一些方法通过POST接受数据并返回带有新uri的303代码在Location头中。

预检OPTIONS请求可以正常工作:

Request Method:OPTIONS
Status Code:200 OK

请求头

Accept:*/*
Accept-Charset:UTF-8,*;q=0.5
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,ru;q=0.6
Access-Control-Request-Headers:origin, authorization, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
DNT:1
Host:api.example
Origin:https://app.example
Referer:https://app.example/app/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.32 (KHTML, like Gecko) Chrome/27.0.1425.0 Safari/537.32 SUSE/27.0.1425.0

响应头

Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Authorization, Content-Type
Access-Control-Allow-Methods:GET,POST,PUT,DELETE,HEAD,OPTIONS
Access-Control-Allow-Origin:https://app.example
Access-Control-Expose-Headers:*
Access-Control-Max-Age:3628800
Connection:keep-alive
Content-Length:0
Date:Sun, 05 May 2013 15:22:50 GMT
Server:nginx/1.2.5

那么实际请求在收到303后就会停止:

Request URL:https://api.example
Request Method:POST
Status Code:HTTP/1.1 303 See Other

响应头:

Server:nginx/1.2.5
Location:https://api.example/some_url
Date:Sun, 05 May 2013 15:27:49 GMT
Content-Type:application/json
Content-Length:0
Connection:keep-alive
Access-Control-Max-Age:3628800
Access-Control-Expose-Headers:*
Access-Control-Allow-Origin:https://app.example
Access-Control-Allow-Methods:GET,POST,PUT,DELETE,HEAD,OPTIONS
Access-Control-Allow-Headers:Authorization, Content-Type
Access-Control-Allow-Credentials:true

RFC规定用户代理应该遵循重定向,但Chrome和FF似乎没有按预期行动。这是浏览器的错误还是我做错了什么?

更新: 如果我使用--disable-web-security启动Chromium一切正常。


你的“真实”请求头是什么(不是CORS预检请求)?我遇到了非常类似的问题。你有解决它的办法吗? - vrutberg
2
看起来这个Chromium的错误报告可能与https://code.google.com/p/chromium/issues/detail?id=237490有关。 - vrutberg
我已创建了一个演示此问题的测试用例: http://dev.ubico.se/cors-redirect/ - vrutberg
@Galadog,MSDN测试使用GET请求,这就是为什么它有效的原因。当它是一个POST请求时它会失败。 - Matthias
显示剩余3条评论
2个回答

16

我也一直在努力解决这个问题。看起来,针对预检的CORS请求进行3xx重定向是不被规范允许的。

http://www.w3.org/TR/cors/

从规范中可以看到:

(步骤1和2详细说明了预检过程。然后我们来到第三步...)

...3. 这是实际的请求。执行“发出请求”步骤,并在进行请求时遵守下面的请求规则

如果响应具有301、302、303、307或308的HTTP状态码,则执行缓存和网络错误步骤

然后,如果我们向下滚动至http://www.w3.org/TR/cors/#cache-and-network-error-steps

每当应用网络错误步骤时,请终止调用此步骤集合的算法,并将跨源请求状态设置为网络错误。

注意:这不会影响用户凭证的设置。即使未设置阻止cookie标志,响应仍会设置cookie。

每当应用缓存和网络错误步骤时,请执行以下步骤:

删除预检结果缓存中源字段值为源起点且url字段值为请求URL的区分大小写相匹配的条目。

执行网络错误步骤,就好像调用缓存和网络错误步骤的算法代替调用网络错误步骤。

(强调来自文档。)

然而,对于简单的CORS请求,允许使用3xx重定向。


请注意,当前的CORS规范是https://fetch.spec.whatwg.org/的一部分,现在允许在预检请求之后跟随重定向。有关详细信息,请参见https://dev59.com/C1sW5IYBdhLWcg3wdnFh#39728229中的答案。 - sideshowbarker

2

如果这是Chromium的错误,以下是由Chromium支持提供的可能在您的代码中出现的错误:

  1. 如果同源请求导致重定向到不同的源,请勿对重定向响应本身执行访问控制检查,因为导致重定向的请求也是同源的。

  2. 如果同源请求导致重定向到不同的源,则请使用原始请求的URL作为新请求的来源,而不要使用唯一的安全源。

  3. 跟踪客户端(即XMLHttpRequest)是否实际上请求发送凭据。当同源请求重定向到不同的源时,原始请求将发送cookie,无论是否请求了它们,因为它们是同源的。除非请求了它们,否则新的跨源请求不应该发送cookie,以便在服务器授予“Access-Control-Allow-Origin = *”时,对响应执行访问控制检查成功。


1
我认为他的问题不是涉及同源请求,而是跨域请求出现了302响应。 - Matthias

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