【问题标题】:HTTP Auth request fails in browser due to CORS, ok in Node由于 CORS,HTTP Auth 请求在浏览器中失败,在 Node 中正常
【发布时间】:2018-06-21 10:38:16
【问题描述】:

我一直在尝试诊断一个问题,即使用 HTTP Basic Auth 的 GET 请求在 Node 中成功,但在浏览器中失败。该问题直接表现为 CORS 故障(401 页面上没有 Access-Control-Allow-Origin)。

request.js:119 OPTIONS http://HOST?PARAMS 401 (Unauthorized)
ClientRequest._onFinish @ request.js:119
(anonymous) @ request.js:61
...

17:53:59.170 localhost/:1 Failed to load http://HOST?PARAMS: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9966' is therefore not allowed access. The response had HTTP status code 401. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

请求如下所示:

const request = require("request-promise");
const options = {"url":"http://...?","auth":{"user":"...","pass":"..."},"json":true,"qs":{...},"headers":{},"resolveWithFullResponse":true}
request.get(options).then(...);

此请求在 Chrome 中的显示方式:

让我惊讶的是:

  • 方法是 OPTIONS,而不是 GET。 (我没有明确要求。)
  • 我的身份验证凭据不包含在标题中(即使我没有设置sendImmediately: false
  • “显示临时标题”警告。

我想知道的:

  • 这是预期的行为吗?
  • 如果不是,怎么回事?
  • 如果是,出了什么问题? :)

在命令行上运行等效的请求似乎工作正常:

curl -I 'http://MYUSERNAME:MYPASSWORD@SAMEURL?SAME=PARAMETERS' -H 'Origin: http://localhost:4466' -X OPTIONS
HTTP/1.1 200 OK
Date: Wed, 20 Jun 2018 07:29:28 GMT
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:4466
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Vary: Origin
X-Frame-Options: SAMEORIGIN
Allow: GET,HEAD,POST,OPTIONS
Content-Length: 0

但是我注意到,如果没有 URL 中提供的凭据,响应中就没有 CORS 标头:

HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jun 2018 07:45:26 GMT
Server: Apache/2.4.29 (Unix) OpenSSL/1.0.2l
WWW-Authenticate: Basic realm="Authentication Required"
Content-Length: 381
Content-Type: text/html; charset=iso-8859-1

这是正确的行为吗?

假设

我怀疑:

  • 浏览器正在发送 OPTIONS “飞行前”请求,因为它必须发送,因为该请求不是“简单请求”。 (不完全清楚这是请求库还是浏览器本身这样做)
  • 服务器响应 401,因为未提供凭据,但未添加应有的 CORS 标头。
  • 如果没有 CORS 标头,浏览器会阻止请求库访问结果,因此无法完成身份验证。

我不确定:

  • 服务器是否配置错误?
  • 请求是否未能在 OPTIONS 预检请求中传递凭据?

【问题讨论】:

  • 服务器配置错误。它没有正确启用 CORS。要正确启用 CORS,它必须使用 200 或 204 状态代码响应未经身份验证的 OPTIONS 请求。请求不是“未能”在 OPTIONS 预检请求中传递凭据。 Request 甚至没有发出 OPTIONS 请求。相反,浏览器引擎是独立的。 CORS 规范要求浏览器从 OPTIONS 请求中省略所有凭据。因此,这种行为是有意的,设计使然。详细解释见答案stackoverflow.com/a/45406085/441757
  • 谢谢,太棒了。你想把它作为一个答案让我接受吗?

标签: javascript cors xmlhttprequest node-request request-promise


【解决方案1】:

服务器配置错误。它没有正确启用 CORS。要正确启用 CORS,它必须使用 200204 状态代码响应未经身份验证的 OPTIONS 请求。

RequestOPTIONS 预检请求中没有“失败”传递凭据。 Request 甚至没有发出 OPTIONS 请求。相反,浏览器引擎是独立的。

在您自己的代码中为请求指定凭据标志不会导致凭据被添加到预检OPTIONS 请求中。相反,凭据只会从您自己的代码中添加到 GET 请求中——也就是说,如果预检成功并且浏览器实际上会继续发出 GET 请求。

这是因为 Fetch 中的 CORS 协议要求指定浏览器必须忽略预检 OPTIONS 请求中的所有凭据。

https://fetch.spec.whatwg.org/#ref-for-credentials%E2%91%A5:

CORS 预检请求从不包含凭据

所以你遇到的行为是有意的,是设计的。

有关更详细的相关解释,请参阅HTTP status code 401 even though I’m sending an Authorization request header的答案。

【讨论】:

  • 谢谢。所以“有凭据”基本上是说“是的,我真的真的想将这些凭据发送到托管在不同域上的这个站点”,否则浏览器会阻止你这样做?
  • 是的。或者换句话说,默认情况下浏览器会忽略凭据。在您的客户端代码中,您必须明确选择让浏览器发送它们。这就是凭证标志的作用。并且您发出请求的服务器还必须选择告诉浏览器实际上可以将凭据从响应中公开给您的前端 JavaScript 代码。这就是 Access-Control-Allow-Credentials 响应标头的用途。有关详细说明,请参阅stackoverflow.com/a/45004550/441757 的答案
  • 超级有帮助。希望我能给你的不仅仅是这个微薄的支持! :)
猜你喜欢
  • 1970-01-01
  • 2016-06-18
  • 2014-09-10
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多