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

2022-08-30 05:05:07

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

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

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

我的问题是,我如何确定jQuery ajax调用是否由于或由于?ERR_INSECURE_RESPONSEERR_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.
    }
});

enter image description here

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

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();

答案 1

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

W3C 规格:

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

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

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

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

如果存在网络错误如果出现 DNS 错误、TLS 协商失败或其他类型的网络错误,请应用网络错误步骤。不要请求任何类型的最终用户交互。

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

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

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


注意:以下示例是使用Google Chrome版本43.0.2357.130针对我创建的模拟OP one的环境制作的。设置它的代码在答案的底部。


我虽然一种方法来解决这个问题是通过HTTP而不是HTTPS发出辅助请求作为这个答案,但我记得这是不可能的,因为较新版本的浏览器会阻止混合内容。

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

自几年前以来一直如此,但是较旧的Web浏览器版本(例如其下面的Mozilla Firefox)版本23允许它。

关于它的证据:

从 HTTPS usign Web Broser 控制台发出 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://localhost:8000/”的页面是通过 HTTPS 加载的,但请求了不安全的 XMLHttpRequest 终结点“http://localhost:8001/”。此请求已被阻止;内容必须通过 HTTPS 提供。

如果您尝试以其他方式执行此操作,则浏览器控制台中将出现相同的错误,就像添加 Iframe 一样。

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

使用Socket连接也被发布为答案,我很确定结果将是相同/相似的,但我已经试过了。

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

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

1)混合内容:“https://localhost:8000/”的页面是通过HTTPS加载的,但试图连接到不安全的WebSocket端点“ws://localhost:8001/”。此请求已被阻止;此终结点必须可通过 WSS 使用。

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

然后,我尝试连接到 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函数”输出到控制台的错误没有任何提示来区分一个错误。


按照这个答案的建议使用代理可以工作,但前提是“目标”服务器具有公共访问权限。

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

创建节点.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中。

要运行它,只需执行并在终端中(ubuntu示例)。node applicationServer.jsnode targetServer.js


答案 2

截至目前:没有办法在浏览器之间区分此事件。由于浏览器不提供事件供开发人员访问。(2015年7月)

这个答案只是试图为一个潜在的,albiet hacky和不完整的解决方案提供想法。


免责声明:这个答案是不完整的,因为它不能完全解决OP的问题(由于跨源政策)。然而,这个想法本身确实有一些优点,可以通过以下方式进一步扩展:@artur grzesiak在这里,使用代理和ajax。


经过我自己的大量研究,似乎没有任何形式的错误检查来检查连接被拒绝和不安全响应之间的区别,至少就javascript为两者之间的差异提供响应而言。

我的研究的普遍共识是SSL证书由浏览器处理,因此在用户接受自签名证书之前,浏览器会锁定所有请求,包括状态代码的请求。浏览器可以(如果编码为)发送回它自己的状态代码以获得不安全的响应,但这并没有真正的帮助,即使这样,你也会遇到浏览器兼容性问题(chrome /firefox/ IE有不同的标准......再次)

由于您最初的问题是在启动与未接受的证书之间检查服务器的状态,因此您不能像这样发出标准HTTP请求吗?

isUp = false;
isAccepted = false;

var isUpRequest = new XMLHttpRequest();
isUpRequest.open('GET', "http://localhost/custom/server/", true); //note non-ssl port
isUpRequest.onload = function() {
    isUp = true;
    var isAcceptedRequest = new XMLHttpRequest();
    isAcceptedRequest.open('GET', "https://localhost/custom/server/", true); //note ssl port
    isAcceptedRequest.onload = function() {
        console.log("Server is up and certificate accepted");
        isAccepted = true;
    }
    isAcceptedRequest.onerror = function() {
        console.log("Server is up and certificate is not accepted");
    }
    isAcceptedRequest.send();
};
isUpRequest.onerror = function() {
    console.log("Server is down");
};
isUpRequest.send();

当然,这确实需要一个额外的请求来验证服务器连接,但它应该通过消除过程完成工作。虽然仍然感觉很笨拙,但我不是双重请求的忠实粉丝。