确定 ajax 调用是否由于响应不安全或连接被拒绝而失败

2023-12-21

我做了很多研究,但找不到处理这个问题的方法。我正在尝试从 https 服务器到使用自定义自签名证书运行 jetty 的 locahost https 服务器执行 jQuery ajax 调用。我的问题是我无法确定响应是连接被拒绝还是不安全响应(由于缺乏证书接受)。有没有办法确定这两种情况之间的差异?这responseText, and statusCode在这两种情况下总是相同的,即使在 chrome 控制台中我可以看到差异:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText总是“”并且statusCode对于这两种情况始终为“0”。

我的问题是,如何确定 jQuery ajax 调用是否由于以下原因失败ERR_INSECURE_RESPONSE或由于ERR_CONNECTION_REFUSED?

一旦证书被接受,一切都会正常工作,但我想知道本地主机服务器是否已关闭,或者它已启动并正在运行,但证书尚未被接受。

$.ajax({
    type: 'GET',
    url: "https://localhost/custom/server/",
    dataType: "json",
    async: true,
    success: function (response) {
        //do something
    },
    error: function (xhr, textStatus, errorThrown) {
        console.log(xhr, textStatus, errorThrown); //always the same for refused and insecure responses.
    }
});

即使手动执行请求,我也会得到相同的结果:

var request = new XMLHttpRequest();
request.open('GET', "https://localhost/custom/server/", true);
request.onload = function () {
    console.log(request.responseText);
};
request.onerror = function () {
    console.log(request.responseText);
};
request.send();

没有办法将其与最新的 Web 浏览器区分开来。

W3C 规范:

以下步骤描述了用户代理对于简单的跨域请求必须执行的操作:

应用发出请求步骤并在发出请求时遵守以下请求规则。

如果未设置手动重定向标志并且响应的 HTTP 状态代码为 301、302、303、307 或 308应用重定向步骤。

如果最终用户取消请求应用中止步骤。

如果出现网络错误如果出现 DNS 错误、TLS 协商失败或其他类型的网络错误,请应用网络错误步骤 http://www.w3.org/TR/cors/#network-error-steps。不请求任何类型的最终用户交互。

注意:这不包括指示某种类型错误的 HTTP 响应,例如 HTTP 状态代码 410。

否则执行资源共享检查。如果返回失败,请应用网络错误步骤。否则,如果返回pass,则终止该算法,并将跨域请求状态设置为成功。实际上并不终止请求。

正如您所读到的,网络错误不包括包含错误的 HTTP 响应,这就是为什么您将始终获得 0 作为状态代码,以及“”作为错误。

Source http://www.w3.org/TR/cors/#simple-cross-origin-request


Note:以下示例是使用 Google Chrome 版本 43.0.2357.130 并针对我为模拟 OP 一而创建的环境制作的。设置它的代码位于答案的底部。


我认为解决这个问题的方法是通过 HTTP 而不是 HTTPS 发出辅助请求这个答案 https://stackoverflow.com/a/31148335/3219633但我记得这是不可能的,因为较新版本的浏览器会阻止混合内容。

这意味着如果您使用 HTTPS,Web 浏览器将不允许通过 HTTP 发出请求,反之亦然。

几年前就出现了这种情况,但较旧的 Web 浏览器版本(例如低于 23 版本的 Mozilla Firefox)允许这样做。

关于它的证据:

使用 Web 浏览器控制台从 HTTPS 发出 HTTP 请求

var request = new XMLHttpRequest();
request.open('GET', "http://localhost:8001", true);
request.onload = function () {
    console.log(request.responseText);
};
request.onerror = function () {
    console.log(request.responseText);
};
request.send();

将导致以下错误:

混合内容:页面位于 'https://本地主机:8000/ https://localhost:8000/'通过 HTTPS 加载,但请求了不安全的 XMLHttpRequest 端点 'http://本地主机:8001/ http://localhost:8001/'。该请求已被阻止;内容必须通过 HTTPS 提供。

如果您尝试以添加 Iframe 等其他方式执行此操作,浏览器控制台中也会出现相同的错误。

<iframe src="http://localhost:8001"></iframe>

使用Socket连接也是作为答案发布 https://stackoverflow.com/a/31148910/3219633,我很确定结果会相同/相似,但我已经尝试过。

尝试使用 HTTPS 从 Web 浏览器打开到非安全套接字端点的套接字连接将导致混合内容错误。

new WebSocket("ws://localhost:8001", "protocolOne");

1) 混合内容:页面位于 'https://本地主机:8000/ https://localhost:8000/' 通过 HTTPS 加载,但尝试连接到不安全的 WebSocket 端点 'ws://localhost:8001/'。该请求已被阻止;此端点必须可通过 WSS 使用。

2) 未捕获的 DOMException:无法构造“WebSocket”:可能无法从通过 HTTPS 加载的页面发起不安全的 WebSocket 连接。

然后我尝试连接到 wss 端点,看看是否可以读取有关网络连接错误的一些信息:

var exampleSocket = new WebSocket("wss://localhost:8001", "protocolOne");
exampleSocket.onerror = function(e) {
    console.log(e);
}

在服务器关闭的情况下执行上面的代码片段会导致:

与“wss://localhost:8001/”的 WebSocket 连接失败:连接建立时出错:net::ERR_CONNECTION_REFUSED

在服务器打开的情况下执行上面的代码片段

与“wss://localhost:8001/”的 WebSocket 连接失败:WebSocket 打开握手已取消

但同样,“onerror 函数”输出到控制台的错误没有任何提示来区分另一个错误。


使用代理作为这个答案建议 https://stackoverflow.com/a/31366144/3219633%5C%5C可以工作,但前提是“目标”服务器具有公共访问权限。

这里的情况并非如此,因此尝试在这种情况下实现代理将导致我们遇到同样的问题。

创建 Node.js HTTPS 服务器的代码:

我创建了两个使用自签名证书的 Nodejs HTTPS 服务器:

目标服务器.js:

var https = require('https');
var fs = require('fs');

var options = {
    key: fs.readFileSync('./certs2/key.pem'),
    cert: fs.readFileSync('./certs2/key-cert.pem')
};

https.createServer(options, function (req, res) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.writeHead(200);
    res.end("hello world\n");
}).listen(8001);

应用程序服务器.js:

var https = require('https');
var fs = require('fs');

var options = {
    key: fs.readFileSync('./certs/key.pem'),
    cert: fs.readFileSync('./certs/key-cert.pem')
};

https.createServer(options, function (req, res) {
    res.writeHead(200);
    res.end("hello world\n");
}).listen(8000);

为了使其工作,您需要安装 Nodejs,需要为每个服务器生成单独的证书并将其相应地存储在文件夹 certs 和 certs2 中。

要运行它只需执行node applicationServer.js and node targetServer.js在终端中(以 ubuntu 为例)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

确定 ajax 调用是否由于响应不安全或连接被拒绝而失败 的相关文章

随机推荐