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

我做了很多研究,但是没有办法解决这个问题。我正在尝试执行一个 jQuery ajax 调用,从 https 服务器到一个本地主机 https 服务器,这个本地主机使用一个自定义的自签名证书运行 jetty。我的问题是,我无法确定响应是拒绝连接还是不安全响应(由于缺乏证书接受)。有没有办法确定这两种情况之间的区别?responseTextstatusCode在这两种情况下总是相同的,即使在铬控制台我可以看到一个区别:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText is always "" and statusCode is always "0" for both cases.

我的问题是,如何确定 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.
}
});

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();
45077 次浏览

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

这个答案仅仅是为一个潜在的、不完整的、不完整的解决方案提供一些想法。


免责声明: 这个回答是不完整的,因为它不能解决 OP 的问题(由于跨原始策略)。然而,这个想法本身确实有一些优点,这些优点被进一步扩展为: @ artur grzesiak@artur grzesiak@a href = “ https://stackoverflow. com/a/31366144/3629438”,使用一个代理和 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();

当然,这确实需要额外的请求来验证服务器连接性,但是它应该通过消除过程来完成任务。不过我还是觉得有点古怪,而且我也不太喜欢双重请求。

@ Schultzie 的回答非常接近,但是很明显,http-一般来说-在浏览器环境中不能从 https工作。

但是,您可以使用中间服务器(代理)代表您发出请求。代理应该允许转发来自 httpshttp请求,或者从 self-signed origins加载内容。

在您的情况下,拥有具有适当证书的自己的服务器可能有些夸张——因为您可以使用这个设置而不是具有自签名证书的机器——但是有大量的 匿名开放代理服务

所以我想到了两种方法:

  1. Ajax request ——在这种情况下,代理必须使用适当的 CORS 设置
  2. 使用 iframe ——通过代理在 iframe 中加载脚本(可能包装在 html 中)。一旦加载了脚本,它就会向其 .parentWindow发送一条消息。如果您的窗口接收到一条消息,您可以确定服务器正在运行(或者更准确地说,在此之前只运行了几分之一秒)。

如果你只对你的本地环境感兴趣,你可以尝试运行与 --disable-web-security标志铬。


另一个建议是: 你有没有尝试通过编程方式加载一个图像来查看那里是否有更多的信息?

检查 < a href = “ http://api.jquery.com/ajaxError/”rel = “ nofollow norefrer”> jQuery.ajaxError () 参考自: JQuery AJAX 错误处理(HTTP 状态代码) 它捕获全局 Ajax 错误,您可以通过 HTTP 或 HTTPS 以任意方式处理这些错误:

if (jqXHR.status == 501) {
//insecure response
} else if (jqXHR.status == 102) {
//connection refused
}

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

W3C 规格:

The steps below describe what user agents must do for a simple cross-origin request:

应用 make a request 步骤,并在发出请求时遵守下面的请求规则。

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

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

If there is a network error In case of DNS errors, TLS negotiation failure, or other type of network errors, apply the 网络错误步骤. Do not request any kind of end user interaction.

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

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

As you can read, network errors does not include HTTP response that include errors, that is why you will get always 0 as status code, and "" as error.

来源


注意 : 下面的例子是使用 Google Chrome Version 43.0.2357.130和我为模拟 OP one 而创建的环境制作的。设置的代码在答案的底部。


I though that an approach To work around this would be make a secondary request over HTTP instead of HTTPS as 这个答案 but I've remembered that is not possible due that newer versions of browsers block mixed content.

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

几年前就是这样了,但是旧版本的浏览器,比如23版本的 MozillaFirefox,允许这样做。

相关证据:

从 HTTPS 使用 Web 浏览器控制台发出 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 函数”输出到控制台的错误没有任何提示来区分一个错误和另一个错误。


使用代理作为 这个答案表明可以工作,但只有在“目标”服务器具有公共访问权限的情况下。

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

创建 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);

ApplicationServer.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.jsnode targetServer.js(ubuntu 示例)。

Unfortunately the present-day browser XHR API does not provide an explicit indication for when the browser refuses to connect due to an "insecure response", and also when it does not trust the website's HTTP/SSL certificate.

但是有办法解决这个问题。

我想出了一个解决方案来确定浏览器什么时候不信任 HTTP/SSL 证书,那就是首先检测是否发生了 XHR 错误(例如使用 jQuery error()回调) ,然后检查 XHR 调用是否指向一个‘ https://’URL,然后检查 XHR readyState是否为0,这意味着 XHR 连接甚至没有被打开(当浏览器不喜欢这个证书时就会发生这种情况)。

下面是我这样做的代码: Https://github.com/maratbn/rainbowpaypress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#l88

我不认为现在有一种方法可以检测这些错误消息,但是你可以做的是在你的应用服务器前使用一个像 nginx 这样的服务器,这样如果应用服务器关闭了,你会从 nginx 得到一个坏的网关错误,带有 502状态代码,你可以在 JS 中检测到。否则,如果证书无效,您仍然会得到与 statusCode = 0相同的通用错误。