Browse Source

REPORT-62882 websocket攻坚落地(远程设计适配)

feature/x
hades 3 years ago
parent
commit
596486c056
  1. 98
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/AbstractSocketConfig.java
  2. 26
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfo.java
  3. 43
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfoFactory.java
  4. 57
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/ContainerSocketConfig.java
  5. 87
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java
  6. 38
      designer-realize/src/main/java/com/fr/design/mainframe/socketio/IOSocketConfig.java

98
designer-realize/src/main/java/com/fr/design/mainframe/socketio/AbstractSocketConfig.java

@ -0,0 +1,98 @@
package com.fr.design.mainframe.socketio;
import com.fr.decision.webservice.utils.DecisionServiceConstants;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.report.RemoteDesignConstants;
import com.fr.stable.StringUtils;
import com.fr.third.org.apache.http.conn.ssl.NoopHostnameVerifier;
import com.fr.third.org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import com.fr.third.org.apache.http.ssl.SSLContexts;
import com.fr.web.WebSocketConfig;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.base.WorkspaceConstants;
import com.fr.workspace.connect.WorkspaceConnection;
import com.fr.workspace.connect.WorkspaceConnectionInfo;
import io.socket.client.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.SSLContext;
/**
* @author hades
* @version 11.0
* Created by hades on 2021/11/29
*/
public abstract class AbstractSocketConfig {
public static final String HTTPS = "https";
public static final String HTTP = "http";
/**
* 当前webSocket选择的协议
*/
private String currentProtocol;
protected abstract String getCurrentProtocolFromUrl(URL url);
protected abstract Integer[] getPorts();
public IO.Options createOptions() {
IO.Options options = new IO.Options();
options.path = WebSocketConfig.getInstance().getSocketContext();
try {
if (ComparatorUtils.equals(currentProtocol, HTTPS)) {
options.sslContext = getSSLContext();
options.hostnameVerifier = NoopHostnameVerifier.INSTANCE;
options.secure = true;
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return options;
}
private SSLContext getSSLContext() throws Exception {
WorkspaceConnectionInfo connection = DesignerSocketIO.getConnectionInfo();
String certPath = connection.getCertPath();
String certSecretKey = connection.getCertSecretKey();
if (StringUtils.isBlank(certPath) || StringUtils.isBlank(certSecretKey)) {
return SSLContexts.createDefault();
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream keystore = new FileInputStream(new File(certPath))) {
trustStore.load(keystore, certSecretKey.toCharArray());
}
return SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build();
}
public String[] getUri() throws IOException {
Workspace current = WorkContext.getCurrent();
URL url = new URL(current.getPath());
Integer[] ports = getPorts();
WorkspaceConnection connection = current.getConnection();
currentProtocol = getCurrentProtocolFromUrl(url);
String[] result = new String[ports.length];
for (int i = 0; i < ports.length; i++) {
result[i] = String.format("%s://%s:%s%s?%s=%s&%s=%s",
currentProtocol,
url.getHost(),
ports[i],
WorkspaceConstants.WS_NAMESPACE,
DecisionServiceConstants.WEB_SOCKET_TOKEN_NAME,
connection.getToken(),
RemoteDesignConstants.USER_LOCK_ID,
connection.getId());
}
FineLoggerFactory.getLogger().info("Available ports: {}, current Protocol: {}", Arrays.toString(ports), currentProtocol);
return result;
}
}

26
designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfo.java

@ -0,0 +1,26 @@
package com.fr.design.mainframe.socketio;
/**
* @author hades
* @version 11.0
* Created by hades on 2021/11/29
*/
public class ConnectionInfo {
private final String uri;
private final AbstractSocketConfig socketConfig;
public ConnectionInfo(String uri, AbstractSocketConfig socketConfig) {
this.uri = uri;
this.socketConfig = socketConfig;
}
public String getUri() {
return uri;
}
public AbstractSocketConfig getSocketConfig() {
return socketConfig;
}
}

43
designer-realize/src/main/java/com/fr/design/mainframe/socketio/ConnectionInfoFactory.java

@ -0,0 +1,43 @@
package com.fr.design.mainframe.socketio;
import com.fr.rpc.ExceptionHandler;
import com.fr.rpc.RPCInvokerExceptionInfo;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.socket.SocketInfoOperator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author hades
* @version 11.0
* Created by hades on 2021/11/29
*/
public class ConnectionInfoFactory {
public static ConnectionInfo[] createConnectionInfo() throws IOException {
List<ConnectionInfo> connectionInfos = new ArrayList<>();
boolean useJavaxWebsocket = WorkContext.getCurrent().get(SocketInfoOperator.class, new ExceptionHandler() {
@Override
public Object callHandler(RPCInvokerExceptionInfo exceptionInfo) {
return false;
}
}).isUseJavaxWebsocket();
if (useJavaxWebsocket) {
// 新方案配置
addConfig(new ContainerSocketConfig(), connectionInfos);
}
// 老方案配置
addConfig(new IOSocketConfig(), connectionInfos);
return connectionInfos.toArray(new ConnectionInfo[0]);
}
private static void addConfig(AbstractSocketConfig config, List<ConnectionInfo> connectionInfos) throws IOException {
String[] configUri = config.getUri();
for (String uri : configUri) {
connectionInfos.add(new ConnectionInfo(uri, config));
}
}
}

57
designer-realize/src/main/java/com/fr/design/mainframe/socketio/ContainerSocketConfig.java

@ -0,0 +1,57 @@
package com.fr.design.mainframe.socketio;
import com.fr.base.FRContext;
import com.fr.log.FineLoggerFactory;
import com.fr.rpc.ExceptionHandler;
import com.fr.rpc.RPCInvokerExceptionInfo;
import com.fr.stable.ArrayUtils;
import com.fr.web.WebSocketConfig;
import com.fr.web.socketio.WebSocketConstants;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.socket.SocketInfoOperator;
import io.socket.client.IO;
import io.socket.engineio.client.transports.Polling;
import io.socket.engineio.client.transports.WebSocket;
import java.net.MalformedURLException;
import java.net.URL;
/**
* 连接基于容器端口的WebSocket
*
* @author hades
* @version 11.0
* Created by hades on 2021/11/29
*/
public class ContainerSocketConfig extends AbstractSocketConfig {
private static final int DEFAULT_SERVER_PORT = 80;
@Override
public IO.Options createOptions() {
IO.Options options = super.createOptions();
options.transports = new String[]{WebSocket.NAME, Polling.NAME};
options.path = String.format("/%s%s", FRContext.getCommonOperator().getAppName(), WebSocketConstants.WEBSOCKET_PATH);
return options;
}
@Override
protected String getCurrentProtocolFromUrl(URL url) {
return url.getProtocol();
}
@Override
protected Integer[] getPorts() {
Integer[] ports = WebSocketConfig.getInstance().getPort();
int serverPort = DEFAULT_SERVER_PORT;
URL url = null;
try {
url = new URL(WorkContext.getCurrent().getPath());
} catch (MalformedURLException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
if (url != null && url.getPort() > 0) {
serverPort = url.getPort();
}
return ArrayUtils.insert(0, ports, serverPort);
}
}

87
designer-realize/src/main/java/com/fr/design/mainframe/socketio/DesignerSocketIO.java

@ -1,7 +1,6 @@
package com.fr.design.mainframe.socketio; package com.fr.design.mainframe.socketio;
import com.fr.config.RemoteConfigEvent; import com.fr.config.RemoteConfigEvent;
import com.fr.decision.webservice.utils.DecisionServiceConstants;
import com.fr.design.DesignerEnvManager; import com.fr.design.DesignerEnvManager;
import com.fr.design.EnvChangeEntrance; import com.fr.design.EnvChangeEntrance;
import com.fr.design.dialog.FineJOptionPane; import com.fr.design.dialog.FineJOptionPane;
@ -19,34 +18,24 @@ import com.fr.design.ui.util.UIUtil;
import com.fr.design.utils.BrowseUtils; import com.fr.design.utils.BrowseUtils;
import com.fr.event.EventDispatcher; import com.fr.event.EventDispatcher;
import com.fr.general.CloudCenter; import com.fr.general.CloudCenter;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory; import com.fr.log.FineLoggerFactory;
import com.fr.report.RemoteDesignConstants;
import com.fr.serialization.SerializerHelper; import com.fr.serialization.SerializerHelper;
import com.fr.stable.ArrayUtils; import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils; import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.third.apache.log4j.spi.LoggingEvent; import com.fr.third.apache.log4j.spi.LoggingEvent;
import com.fr.third.org.apache.http.client.config.RequestConfig; import com.fr.third.org.apache.http.client.config.RequestConfig;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse; import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.client.methods.HttpGet; import com.fr.third.org.apache.http.client.methods.HttpGet;
import com.fr.third.org.apache.http.conn.ssl.NoopHostnameVerifier;
import com.fr.third.org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient; import com.fr.third.org.apache.http.impl.client.CloseableHttpClient;
import com.fr.third.org.apache.http.impl.client.HttpClients; import com.fr.third.org.apache.http.impl.client.HttpClients;
import com.fr.third.org.apache.http.ssl.SSLContexts;
import com.fr.web.WebSocketConfig;
import com.fr.web.socketio.WebSocketProtocol;
import com.fr.workspace.WorkContext; import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace; import com.fr.workspace.Workspace;
import com.fr.workspace.base.WorkspaceConstants; import com.fr.workspace.base.WorkspaceConstants;
import com.fr.workspace.connect.WorkspaceConnection;
import com.fr.workspace.connect.WorkspaceConnectionInfo; import com.fr.workspace.connect.WorkspaceConnectionInfo;
import io.socket.client.IO; import io.socket.client.IO;
import io.socket.client.Socket; import io.socket.client.Socket;
import io.socket.emitter.Emitter; import io.socket.emitter.Emitter;
import javax.net.ssl.SSLContext;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -55,13 +44,8 @@ import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.Timer; import java.util.Timer;
public class DesignerSocketIO { public class DesignerSocketIO {
@ -80,8 +64,8 @@ public class DesignerSocketIO {
private static Timer disConnectHintTimer = null; private static Timer disConnectHintTimer = null;
private static long disConnectHintTimerDelay = 3000; private static long disConnectHintTimerDelay = 3000;
private static final int TIMEOUT = 5000; private static final int TIMEOUT = 5000;
//维护一个当前工作环境的uri列表 //维护一个当前工作环境的websocket连接信息列表
private static String[] uri; private static ConnectionInfo[] connectionInfos;
//维护一个关于uri列表的计数器 //维护一个关于uri列表的计数器
private static int count; private static int count;
// 当前webSocket选择的协议 // 当前webSocket选择的协议
@ -104,7 +88,7 @@ public class DesignerSocketIO {
} }
//每当更换工作环境,更新uri列表,同时更新计数器count //每当更换工作环境,更新uri列表,同时更新计数器count
try { try {
uri = getSocketUri(); connectionInfos = ConnectionInfoFactory.createConnectionInfo();
} catch (IOException e) { } catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e); FineLoggerFactory.getLogger().error(e.getMessage(), e);
} }
@ -116,8 +100,9 @@ public class DesignerSocketIO {
private static void createSocket() { private static void createSocket() {
//根据uri和计数器建立连接,并注册监听 //根据uri和计数器建立连接,并注册监听
try { try {
if (count < uri.length) { if (count < connectionInfos.length) {
socket = IO.socket(new URI(uri[count]), createOptions()); ConnectionInfo connectionInfo = connectionInfos[count];
socket = IO.socket(new URI(connectionInfo.getUri()), connectionInfo.getSocketConfig().createOptions());
socket.on(WorkspaceConstants.WS_LOGRECORD, printLog); socket.on(WorkspaceConstants.WS_LOGRECORD, printLog);
socket.on(WorkspaceConstants.CONFIG_MODIFY, modifyConfig); socket.on(WorkspaceConstants.CONFIG_MODIFY, modifyConfig);
socket.on(Socket.EVENT_CONNECT_ERROR, failRetry); socket.on(Socket.EVENT_CONNECT_ERROR, failRetry);
@ -135,37 +120,7 @@ public class DesignerSocketIO {
} }
} }
private static IO.Options createOptions() { public static WorkspaceConnectionInfo getConnectionInfo() {
IO.Options options = new IO.Options();
try {
if (ComparatorUtils.equals(currentProtocol, HTTPS)) {
options.sslContext = getSSLContext();
options.hostnameVerifier = NoopHostnameVerifier.INSTANCE;
options.secure = true;
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return options;
}
private static SSLContext getSSLContext() throws Exception {
WorkspaceConnectionInfo connection = getConnectionInfo();
String certPath = connection.getCertPath();
String certSecretKey = connection.getCertSecretKey();
if (StringUtils.isBlank(certPath) || StringUtils.isBlank(certSecretKey)) {
return SSLContexts.createDefault();
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream keystore = new FileInputStream(new File(certPath))) {
trustStore.load(keystore, certSecretKey.toCharArray());
}
return SSLContexts.custom()
.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build();
}
private static WorkspaceConnectionInfo getConnectionInfo() {
if (DesignerWorkspaceInfoContext.getWorkspaceInfo() == null) { if (DesignerWorkspaceInfoContext.getWorkspaceInfo() == null) {
String currentName = DesignerEnvManager.getEnvManager().getCurEnvName(); String currentName = DesignerEnvManager.getEnvManager().getCurEnvName();
DesignerWorkspaceInfo info = DesignerEnvManager.getEnvManager().getWorkspaceInfo(currentName); DesignerWorkspaceInfo info = DesignerEnvManager.getEnvManager().getWorkspaceInfo(currentName);
@ -175,34 +130,6 @@ public class DesignerSocketIO {
} }
} }
private static String[] getSocketUri() throws IOException {
Workspace current = WorkContext.getCurrent();
URL url = new URL(current.getPath());
Integer[] ports = WebSocketConfig.getInstance().getAvailablePorts();
WorkspaceConnection connection = current.getConnection();
// 服务器配置https webSocket可能是wss也可能是ws webSocket的协议可以单独配置
WebSocketProtocol webSocketProtocol = WebSocketConfig.getInstance().getProtocol();
currentProtocol = webSocketProtocol == WebSocketProtocol.PLAIN ? HTTP : HTTPS;
if (WebSocketConfig.getInstance().isUsingProxy()) {
// 如果配置了代理服务器跟随服务器协议
currentProtocol = url.getProtocol();
}
String[] result = new String[ports.length];
for (int i = 0; i < ports.length; i++) {
result[i] = String.format("%s://%s:%s%s?%s=%s&%s=%s",
currentProtocol,
url.getHost(),
ports[i],
WorkspaceConstants.WS_NAMESPACE,
DecisionServiceConstants.WEB_SOCKET_TOKEN_NAME,
connection.getToken(),
RemoteDesignConstants.USER_LOCK_ID,
connection.getId());
}
FineLoggerFactory.getLogger().info("Available ports: {}, current Protocol: {}", Arrays.toString(ports), currentProtocol);
return result;
}
//失败重试监听器:1、关闭失败的socket 2、计数器加1 3、调用创建socket方法 //失败重试监听器:1、关闭失败的socket 2、计数器加1 3、调用创建socket方法
private static final Emitter.Listener failRetry = new Emitter.Listener() { private static final Emitter.Listener failRetry = new Emitter.Listener() {
@Override @Override

38
designer-realize/src/main/java/com/fr/design/mainframe/socketio/IOSocketConfig.java

@ -0,0 +1,38 @@
package com.fr.design.mainframe.socketio;
import com.fr.web.WebSocketConfig;
import com.fr.web.socketio.WebSocketProtocol;
import io.socket.client.IO;
import java.net.URL;
/**
* @author hades
* @version 11.0
* Created by hades on 2021/11/29
*/
public class IOSocketConfig extends AbstractSocketConfig {
@Override
public IO.Options createOptions() {
IO.Options options = super.createOptions();
options.path = WebSocketConfig.getInstance().getSocketContext();
return options;
}
@Override
protected String getCurrentProtocolFromUrl(URL url) {
// 服务器配置https webSocket可能是wss也可能是ws webSocket的协议可以单独配置
WebSocketProtocol webSocketProtocol = WebSocketConfig.getInstance().getProtocol();
String currentProtocol = webSocketProtocol == WebSocketProtocol.PLAIN ? HTTP : HTTPS;
if (WebSocketConfig.getInstance().isUsingProxy()) {
// 如果配置了代理服务器跟随服务器协议
currentProtocol = url.getProtocol();
}
return currentProtocol;
}
@Override
protected Integer[] getPorts() {
return WebSocketConfig.getInstance().getAvailablePorts();
}
}
Loading…
Cancel
Save