我有一个 javascript API,它包含在一些网站中。由于某些原因,需要将浏览器端与用 Java 编写的桌面应用程序进行通信。正如您所看到的,所有内容都是主机本地的。
我的第一个解决方案是为每个已知浏览器使用插件/扩展,但缺乏良好的支持和大量依赖项阻止我选择该选项。然后我的第二种方法是双方都使用websockets,服务器在Java应用程序中,客户端是浏览器,一切都会很完美,但随后一个大问题浮出水面: HTTPS 网站无法与不安全的 websocket 服务器建立连接,无论 Java 的情况如何,也无论服务器是否位于本地主机上。
我一直在使用的java_websocket API不足以解决这个问题,而在谷歌中,Jetty是一个听起来适合我的目的的好名字。我在几个小时内开始使用 Jetty,在一些示例之后我得到了两个类,一个服务器端点 (EchoSocket) 和服务器 (EventServer):
EventServer.java
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
public class EventServer {
public static void main(String[] args) {
try {
Server server = new Server();
int httpsPort = 8771;
SslContextFactory scf = new SslContextFactory("mykeystore.jks");
scf.setKeyStorePassword("blaStore");
scf.setKeyManagerPassword("blablaKey");
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(scf, "http/1.1");
HttpConfiguration https = new HttpConfiguration();
https.setSecurePort(httpsPort);
https.setSecureScheme("https");
https.addCustomizer(new SecureRequestCustomizer());
ServerConnector sslConnector = new ServerConnector(server,
sslConnectionFactory,
new HttpConnectionFactory(https)
);
sslConnector.setPort(httpsPort);
server.addConnector(sslConnector);
HandlerList baseHandler = new HandlerList();
server.setHandler(baseHandler);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); //ServletContextHandler.SESSIONS
context.setContextPath("/");
baseHandler.addHandler(context);
ServerContainer jsrContainer = WebSocketServerContainerInitializer.configureContext(context);
jsrContainer.addEndpoint(EchoSocket.class);
baseHandler.addHandler(new DefaultHandler());
server.start();
server.dump(System.err);
server.join();
} catch (Throwable ex) {
ex.printStackTrace(System.err);
}
}
}
EchoSocket.java
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value="/echo")
public class EchoSocket
{
@OnMessage
public void onMessage(javax.websocket.Session session, String message){
session.getAsyncRemote().sendText("welcomeeee");
}
@OnError
public void onError(Throwable cause)
{
cause.printStackTrace(System.err);
}
}
使用 Websocket online test我可以从 Mozilla Firefox 和 Opera 进行通信,但几分钟后连接就会断开,有时会出现未定义的错误导致连接关闭。
如果 Microsoft Edge 和 IE 不起作用,当我将 URI 写为 wss://localhost:8771/echo 时错误是:
SCRIPT12017: WebSocket Error: SECURITY_ERR, Cross zone connection not allowed
当我尝试使用 wss://127.0.0.1:8771/echo 时,错误是:
SCRIPT12038: WebSocket Error: Network Error 12038, The host name in the certificate is invalid or does not match
在 Google Chrome 中,这两种 URI 似乎都不起作用,没有连接,错误是:
WebSocket connection to 'wss://127.0.0.1:8771/echo' failed: Error in connection establishment: net::ERR_NETWORK_CHANGED
在另一种情况下:
WebSocket connection to 'wss://localhost:8771/echo' failed: Error in connection establishment: net::ERR_CONNECTION_CLOSED
所以,我需要一些帮助,但也许你可以回答我接下来的一些问题:
- 我的 Jetty 示例正确吗?如果不正确,我该如何更正它?
- 我应该尝试其他库或旧版本的 Jetty 才能在大多数浏览器中使用此功能吗?
- 我应该看看其他语言(例如 C#)中的 Websocket 服务器的其他实现吗?
- 我必须在 SSL 证书中设置什么主机名或主题备用名称才能允许来自 IE 和 Edge 的连接?也许是“localhost”或“127.0.0.1”。
请提前致谢,抱歉我的英语。
最佳答案
根据经验,浏览器中的 WebSocket 通过 JavaScript 无法与浏览器不知道的 wss://
加密主机一起使用(证书未由 CA 签名,该 CA 无法识别该主机)浏览器正在使用)
自签名是不可能的,除非您也将证书链添加到浏览器。
这主要是因为它是一个 API,因此没有 GUI 允许您接受证书以绕过浏览器中的安全限制(对于不属于它所了解的 CA 的证书)
Note: Sometimes (but not always) you can get around this certificate restriction, temporarily, by having a normal
https://localhost/index.html
lookup before you attempt to access thewss://localhost...
, allowing you to accept the Certificate for that session.
其他一些新闻:
- Chrome is going to ban sub-resource loading (即:
wss://localhost...
) - Firefox 有 open bug关于使用
wss://localhost...
。
关于javascript - JavaScript 和 jetty 嵌入式 Websocket 服务器之间的 Websocket 困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39132273/