2015-06-25 10 views
111

我一直在进行大量的研究并找不到处理此问题的方法。我正尝试从https服务器执行jQuery ajax调用,并通过自定义自签名证书运行带有jetty的locahost https服务器。我的问题是我无法确定响应是拒绝连接还是不安全响应(由于缺少证书接受)。有没有办法来确定两种情况之间的差异?该responseText,和statusCode总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别:确定是否由于不安全的响应或拒绝连接而导致ajax呼叫失败

net::ERR_INSECURE_RESPONSE 
net::ERR_CONNECTION_REFUSED 

responseText始终是“”和statusCode始终为“0”两种情况。

我的问题是,如何确定是否由于ERR_INSECURE_RESPONSE或由于ERR_CONNECTION_REFUSED而导致jQuery ajax调用失败?

一旦证书被接受,一切正常,但我想知道本地主机服务器是关闭,还是启动并运行,但证书尚未被接受。

$.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(); 
+6

我会发布我的代码,但它不是一个javascript代码错误。请仔细阅读我的问题。 – taxicala

+1

另外两个错误回调参数是否给出了额外的见解? 'function(xhr,status,msg){...'我怀疑他们会这样做,但值得尝试。 –

+0

您是否在不同的浏览器中看到不同的错误代码?这可能是由Chrome生成和阻止的。 – Jasen

回答

59

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

W3C规范:

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

Apply the make a request steps and observe the request rules below while making the request.

If the manual redirect flag is unset and the response has an HTTP status code of 301, 302, 303, 307, or 308 Apply the redirect steps.

If the end user cancels the request Apply the abort steps.

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

Note: This does not include HTTP responses that indicate some type of error, such as HTTP status code 410.

Otherwise Perform a resource sharing check. If it returns fail, apply the network error steps. Otherwise, if it returns pass, terminate this algorithm and set the cross-origin request status to success. Do not actually terminate the request.

正如你可以看到,网络错误不包括HTTP响应,其中包括错误的,这就是为什么你总是0获得作为状态码,而“”的错误。

Source


注意:使用谷歌的Chrome版本43.0.2357.130作了下面的例子和反对,我已经可以模拟OP一个环境。代码设置在答案的底部。


我虽然是一种方法来解决,这将是使通过HTTP的二次请求,而不是HTTPS作为This answer,但我记得这是不可能的,因为该浏览器的新版本阻止混合内容。

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

从几年前就已经是这样了,但老版本的Web浏览器版本比如Mozilla Firefox在它下面版本23允许它。

证据看:

使从HTTP请求HTTPS usign网络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(); 

将导致以下错误:

Mixed Content: The page at ' https://localhost:8000/ ' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint ' http://localhost:8001/ '. This request has been blocked; the content must be served over HTTPS.

同样的错误将出现在浏览器控制台,如果您尝试以其他方式添加Iframe来完成此操作。

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

使用Socket连接也Posted as an answer,我敢肯定,其结果将是相同/相似的,但我已经给它一个尝试。

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

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

1) Mixed Content: The page at ' https://localhost:8000/ ' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://localhost:8001/'. This request has been blocked; this endpoint must be available over WSS.

2) Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

然后我试图连接到WSS端点也看到如果我可以阅读有关网络连接错误的一些信息:

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

执行上面的代码中使用服务器关闭的结果:

WebSocket connection to 'wss://localhost:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

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

WebSocket connection to 'wss://localhost:8001/' failed: WebSocket opening handshake was canceled

但是,“onerror函数”输出到控制台的错误没有任何技巧来区分另一个错误。


使用代理作为this answer suggest可以工作,但只有在“目标”服务器的公共访问。

这不是这种情况,所以试图在这种情况下实现代理将导致我们遇到同样的问题。

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

我已经创建了两个HTTPS的NodeJS服务器,使用自签名证书:

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

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中。

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

+5

这是迄今为止最完整的答案。它表明你实际上已经完成了一些研究工作和测试。我看到你已经尝试过所有可能的方式来执行此操作,并且所有场景都很好解释。您还提供了示例代码来快速设置测试环境!真的很好!感谢您的洞察! – taxicala

29

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

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


免责声明:这个答案是不完整的,因为它不完全解决 OP的问题(由于跨域策略)。然而,这个想法本身确实具有一些优点,这些优点被进一步扩展:@artur grzesiak here,使用代理和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(); 

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

+6

我读过整个答案,我不认为这会起作用。我的两台服务器都运行HTTPS,这意味着此时我向HTTP服务器发出请求,浏览器将立即取消它,因为您无法向HTTP服务器发送从HTTPS服务器发出的Ajax请求 – taxicala

11

@ Schultzie的回答非常接近,但显然http - 一般来说 - 在浏览器环境下不会起作用https

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

有适当的证书自己的服务器可能是你的情况矫枉过正 - 你可以使用自签名证书,而不是使用机器此设置 - 但有很多匿名开放代理服务出那里。

这样,我想起这两种方法是:

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

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


另一个建议:你是否尝试以编程方式加载图像,以确定是否存在更多信息?

2

退房jQuery.ajaxError() 取出的参考来源:jQuery AJAX Error Handling (HTTP Status Codes) 它抓住,你可以在任何数量的方式通过HTTP或HTTPS处理全局阿贾克斯的错误:

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

这与做ajax就像我这样做,但集中在一个地方的错误响应。 – taxicala

+1

问题是,当ajax调用发生时,证书没有加载,因为它似乎是问题所在。任何方式gl找到答案:) – Varshaan

+2

不,问题是,我想确定必须接受证书,并知道服务器是否关闭之间的差异。 – taxicala

1

不幸的是,现在的浏览器XHR API没有提供明确的指示,说明浏览器由于“不安全的响应”而拒绝连接,以及它不信任网站的HTTP/SSL证书时。

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

我想出一个解决方案来确定浏览器何时不信任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

1

我不认为有目前一种方法来检测这些错误信息,但黑客工具,你能做的就是在前面使用服务器类似的nginx您的应用程序服务器,以便如果应用程序服务器关闭,您将从nginx获得一个错误的网关错误,您可以在JS中检测到502状态代码。否则,如果证书无效,您仍然会收到与statusCode = 0相同的通用错误。

相关问题