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

tax*_*ala 114 javascript ajax https jquery cors

我一直在做很多研究,但找不到办法解决这个问题.我正在尝试从https服务器执行jQuery ajax调用到运行带有自定义自签名证书的jetty的locahost https服务器.我的问题是我无法确定响应是拒绝连接还是不安全响应(由于缺少证书接受).有没有办法确定两种情景之间的差异?的responseText,并且statusCode总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED
Run Code Online (Sandbox Code Playgroud)

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.
    }
});
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

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();
Run Code Online (Sandbox Code Playgroud)

eca*_*izo 65

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

W3C规格:

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

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

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

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

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

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

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

正如您所读到的,网络错误不包括包含错误的HTTP响应,这就是为什么您将始终为0作为状态代码,并将""作为错误.

资源


注意:以下示例是使用Google Chrome版本43.0.2357.130以及我创建的用于模拟OP的环境.设置的代码位于答案的底部.


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

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

几年前就是这样的,但是像版本23下面的Mozilla Firefox这样的旧版Web浏览器允许它.

关于它的证据:

从HTTPS发出HTTP请求使用Web Broser控制台

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();
Run Code Online (Sandbox Code Playgroud)

将导致以下错误:

混合内容:" https:// localhost:8000 / "页面是通过HTTPS加载的,但是请求了一个不安全的XMLHttpRequest端点' http:// localhost:8001 / '.此请求已被阻止; 内容必须通过HTTPS提供.

如果您尝试以添加Iframe的其他方式执行此操作,则会在浏览器控制台中显示相同的错误.

<iframe src="http://localhost:8001"></iframe>
Run Code Online (Sandbox Code Playgroud)

使用Socket连接也作为答案发布,我很确定结果将是相同/相似的,但我试一试.

尝试使用HTTPS打开从Web Broswer到非安全套接字端点的套接字连接将以混合内容错误结束.

new WebSocket("ws://localhost:8001", "protocolOne");
Run Code Online (Sandbox Code Playgroud)

1)混合内容:' 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);
}
Run Code Online (Sandbox Code Playgroud)

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

与'wss:// localhost:8001 /'的WebSocket连接失败:连接建立错误:net :: ERR_CONNECTION_REFUSED

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

与'wss:// localhost:8001 /'的WebSocket连接失败:WebSocket打开握手被取消

但同样,"onerror功能"输出到控制台的错误没有任何提示来区分另一个错误.


使用代理作为此答案建议可以工作,但前提是"目标"服务器具有公共访问权限.

这不是这种情况,因此在这种情况下尝试实现代理会导致我们遇到同样的问题.

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

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

targetServer.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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 到目前为止,这是最完整的答案.它表明您实际上已经完成了一些研究工作和测试.我看到你已经尝试了所有可能的方法来执行此操作,所有场景都得到了很好的解释.您还提供了示例代码以快速设置测试环境!真的很好的工作!感谢您的见解! (6认同)

acu*_*joe 31

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

这个答案仅仅是为潜在的,混合的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();
Run Code Online (Sandbox Code Playgroud)

当然,这需要额外的请求来验证服务器连接,但它应该通过消除过程完成工作.尽管如此,仍然感觉很酷,而且我不是那个加倍请求的忠实粉丝.

  • 我已经阅读了完整的答案,我认为这不会起作用.我的两台服务器都运行HTTPS,这意味着当我向HTTP服务器发出请求时,浏览器会立即取消它,因为您无法从HTTPS服务器向HTTP服务器发出ajax请求 (7认同)

art*_*iak 11

@ Schultzie的答案非常接近,但很明显http- 一般来说 - https在浏览器环境中不起作用.

您可以做的是使用中间服务器(代理)代表您发出请求.代理应该允许httphttps源转发请求或从自签名源加载内容.

拥有适当证书的自己的服务器在你的情况下可能是一种矫枉过正 - 因为你可以使用这个设置而不是带有自签名证书的机器 - 但是那里有很多匿名的开放代理服务.

因此,我想到的两种方法是:

  1. ajax请求 - 在这种情况下,代理必须使用适当的CORS设置
  2. 使用iframe - 您可以通过代理在iframe中加载脚本(可能包含在html中).加载脚本后,它会向其发送一条消息.parentWindow.如果您的窗口收到一条消息,您可以确定服务器正在运行(或者更确切地说,之前运行的是一小段时间).

如果您只对本地环境感兴趣,可以尝试使用带有--disable-web-security标志的chrome运行.


另一个建议:您是否尝试以编程方式加载图像以查明是否存在更多信息?