Chrome v37/38 CORS(再次)失败,401 用于 OPTIONS 飞行前请求 [英] Chrome v37/38 CORS failing (again) with 401 for OPTIONS pre-flight requests

查看:17
本文介绍了Chrome v37/38 CORS(再次)失败,401 用于 OPTIONS 飞行前请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从 Chrome 版本 37 开始,如果服务器启用了身份验证,即使所有 CORS 标头都已正确设置,预先发送的跨域请求也会失败(再次).这是在 localhost(我的开发 PC)上.

As from Chrome version 37, pre-flighted, cross-domain requests are failing (again) if the server has authentication enabled, even though all CORS headers are set correctly. This is on localhost (my dev PC).

你们中的一些人可能知道 Chrome/CORS/auth 错误的历史,尤其是在涉及 HTTPS 时.我的问题涉及 HTTPS:我有一个 AngularJS 应用程序从 localhost:8383localhost:8081 上的 Java (Jetty) 服务器通信已激活 HTTP BASIC 身份验证.GET 工作正常,但 POST 失败并显示 401:

Some of you may be aware of the history of Chrome/CORS/auth bugs, especially when HTTPS was involved. My problem does not involve HTTPS: I have an AngularJS application served from localhost:8383 talking to a Java (Jetty) server on localhost:8081 that has HTTP BASIC auth activated. GETs work fine, but POSTs fail with a 401:

XMLHttpRequest cannot load http://localhost:8081/cellnostics/rest/patient.
Invalid HTTP status code 401

我之前编写了一个自定义 (Java) CORS 过滤器,用于设置正确的 CORS 标头,直到 v36.它在 v37 和最新的 v38 (38.0.2125.101 m) 中失败.它在 Internet Explorer 11 (11.0.9600) 和 Opera 12.17 (build 1863) 上仍然可以正常工作.

I have previously written a custom (Java) CORS filter that sets the correct CORS headers, which worked up until v36. It fails in v37 and also the latest v38 (38.0.2125.101 m). It still works as expected with Internet Explorer 11 (11.0.9600) and Opera 12.17 (build 1863).

GET 请求成功,但 POST 失败.由于内容类型:application/json",Chrome 似乎正在预检我的所有 POST,而且预检的 OPTIONS 请求失败了.

GET requests succeed, but POSTs fail. It looks like Chrome is pre-flighting all my POSTs due to the content-type: "application/json", and that it is the pre-flighted OPTIONS request that is failing.

在 Angular 应用程序中,我明确设置了以下请求标头.AFAIK withCredentials 的此设置应确保即使对于 OPTIONS 请求也发送凭据:

In the Angular app I explicitly set the following request headers. AFAIK this setting for withCredentials should ensure that credentials are sent even for OPTIONS requests:

//Enable cross domain calls
$httpProvider.defaults.useXDomain = true;

//Send all requests, even OPTIONS, with credentials
$httpProvider.defaults.withCredentials = true;

以下是请求/响应.您可以看到在 Access-Control-Allow-Methods 标头中启用了 OPTIONS 方法.您还可以看到 Javascript 应用的源已明确启用:Access-Control-Allow-Origin: http://localhost:8383.

Below is the request/response. You can see that the OPTIONS method is enabled in the Access-Control-Allow-Methods header. You can also see that the Javascript app's origin is explicitly enabled: Access-Control-Allow-Origin: http://localhost:8383.

Remote Address:[::1]:8081
Request URL:http://localhost:8081/cellnostics/rest/medicaltest
Request Method:OPTIONS
Status Code:401 Full authentication is required to access this resource

Request headers:

Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,af;q=0.6
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:8081
Origin:http://localhost:8383
Referer:http://localhost:8383/celln-web/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36

Response headers:

Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept
Access-Control-Allow-Methods:POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin:http://localhost:8383
Access-Control-Max-Age:3600
Content-Length:0
Server:Jetty(8.1.8.v20121106)
WWW-Authenticate:Basic realm="Cellnostics"

有人知道我还应该做什么吗?我确保在测试、重新启动之前清除 Chrome 缓存,并确保在重新启动之前没有运行任何后台 Chrome 进程,所以我很确定没有挥之不去的身份验证缓存问题.

Has anyone got any idea what else I should do? I made sure to clear the Chrome cache before testing, restarting and ensuring that there were no background Chrome processes left running before restart, so I'm pretty sure that there were no lingering auth cache issues.

我不得不切换到 IE 11 来测试我的 Web 开发.相同的客户端和服务器设置仍然适用于 IE 和 Opera,以及存在 Chrome/CORS 错误历史的事实,让我怀疑 Chrome.

I've had to switch to IE 11 for testing my web development. The fact that the same client and server setup still works for IE and Opera, and the fact that there is a history of Chrome/CORS bugs, makes me suspect Chrome.

这是 Chrome 网络内部事件列表的摘录:

Here's an extract from the Chrome net-internals event list:

t=108514 [st=0]   +URL_REQUEST_START_JOB  [dt=4]
    --> load_flags = 336011264 (BYPASS_DATA_REDUCTION_PROXY | DO_NOT_SAVE_COOKIES | DO_NOT_SEND_AUTH_DATA | DO_NOT_SEND_COOKIES | MAYBE_USER_GESTURE | VERIFY_EV_CERT)
    --> method = "OPTIONS"
    --> priority = "LOW"
    --> url = "http://localhost:8081/cellnostics/rest/patient"
...
t=108516 [st=2] HTTP_TRANSACTION_SEND_REQUEST_HEADERS
--> OPTIONS /cellnostics/rest/patient HTTP/1.1
   Host: localhost:8081
   Connection: keep-alive
   Access-Control-Request-Method: POST
   Origin: http://localhost:8383
   User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36
   Access-Control-Request-Headers: accept, content-type
   Accept: */*
   Referer: http://localhost:8383/celln-web/index.html
   Accept-Encoding: gzip,deflate,sdch
   Accept-Language: en-US,en;q=0.8,af;q=0.6

因此,即使我明确设置了 withCredentials = true,授权标头似乎也没有随 OPTIONS 飞行前发送.

So it looks like the Authorization header is not sent with the OPTIONS pre-flight, even though I explicitly set withCredentials = true.

但是,为什么 IE 和 Opera 仍然可以工作?Chrome 在这方面是否更符合标准?为什么它可以工作,然后从 v37 开始失败?

However, why would IE and Opera still work? Is Chrome more standards-compliant in this regard? Why did it work and then start failing from v37?

Chrome 开发工具未在上述转储中显示请求的 Content-Type,但此处来自网络日志.第一张图片显示了禁用服务器身份验证时的 POST,内容类型正确发送为应用程序/json".第二张图片是启用身份验证时,显示 OPTIONS 请求失败(似乎 OPTIONS 总是以内容类型text/plain"发送?).

Chrome dev tools does not show the Content-Type of the request in the dumps above, but here it is from the Network log. The first pic shows the POST when the server auth is disabled, with content type correctly sent as 'application/json'. The 2nd pic is when the auth is enabled, showing the OPTIONS request failing (it seems OPTIONS is always sent with content type 'text/plain'?).

推荐答案

@Cornel Masson,你解决问题了吗?我不明白为什么您的服务器要求您对 OPTIONS 请求进行身份验证,但我在 SAP NetWeaver 服务器上遇到了同样的问题.我已经阅读了整个 CORS 规范(我推荐),所以我可以澄清你的一些疑问.

@Cornel Masson, did you solve the problem? I do not understand why your server is asking you to authenticate the OPTIONS request, but I am facing this same issue against a SAP NetWeaver server. I have read the whole CORS specification (I recommend) so I can clarify you some of your doubts.

关于你的句子

在 Angular 应用程序中,我明确设置了以下请求标头.AFAIK withCredentials 的此设置应确保即使对于 OPTIONS 请求也发送凭据:

In the Angular app I explicitly set the following request headers. AFAIK this setting for withCredentials should ensure that credentials are sent even for OPTIONS requests:

  • 根据 CORS 规范,当用户代理(因此,abrowser) 预检请求(使用 OPTIONS HTTP 方法的请求),它必须排除用户凭据(cookie、HTTP 身份验证...),因此任何 OPTIONS 请求都不能被请求为经过身份验证.浏览器将请求经过身份验证的实际请求(使用请求的 HTTP 方法,如 GET、POST...),而不是预检请求.
  • 因此浏览器不得在 OPTIONS 请求中发送凭据.他们会在实际请求中做.如果你写 withCredentials = true 浏览器应该按照我说的去做.
    • According to the CORS specification when a user agent (thus, a browser) preflights a request (requests with OPTIONS HTTP method), it MUST exclude the user credentials (cookies, HTTP authentication...) so any OPTIONS request cannot be requested as authenticated. The browser will request as authenticated the actual request (the one with the requested HTTP method like GET, POST...), but not the preflight request.
    • So browsers MUST not send the credentials in OPTIONS request. They will do in actual requests. If you write withCredentials = true the browser should do what I say.
    • 根据你的说法:

      由于内容类型:application/json":

      It looks like Chrome is pre-flighting all my POSTs due to the content-type: "application/json":

      • 规范还说,当标头不是简单标头"时,浏览器将发出预检请求,在这里您可以了解这意味着什么:

        • The specification also says that a preflight request will be made by the browser when the header is not a "simple header" and here you have what that means:

          如果标头字段名称是与 Accept、Accept-Language 或 Content-Language 的 ASCII 不区分大小写匹配,或者如果它是与 Content-Type 和标头字段值媒体类型(不包括参数)是不区分大小写的 ASCII 匹配 application/x-www-form-urlencodedmultipart/form-data文本/纯文本.

        • application/json 不包括在内,因此浏览器必须按原样预检请求.
        • A header is said to be a simple header if the header field name is an ASCII case-insensitive match for Accept, Accept-Language, or Content-Language or if it is an ASCII case-insensitive match for Content-Type and the header field value media type (excluding parameters) is an ASCII case-insensitive match for application/x-www-form-urlencoded, multipart/form-data, or text/plain.

        • application/json is not included so the browser MUST preflight the request as it does.
        • 我刚刚发现一个人有同样的问题,反映了真正的问题,如果你和他使用相同的服务器,你会很幸运,https://evolpin.wordpress.com/2012/10/12/the-cors/

          I just found a person with same problem that reflects the real problems, and if you uses the same server as him you will be lucky, https://evolpin.wordpress.com/2012/10/12/the-cors/

          这篇关于Chrome v37/38 CORS(再次)失败,401 用于 OPTIONS 飞行前请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆