This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 06b525b8d [ISSUE #3794] websocket datasync can chose allow origin to
avoid CSRF (#3795)
06b525b8d is described below
commit 06b525b8d46865e83e0b93936d80623b8582bb3a
Author: zhengpeng <[email protected]>
AuthorDate: Thu Aug 4 11:09:15 2022 +0800
[ISSUE #3794] websocket datasync can chose allow origin to avoid CSRF
(#3795)
* feature: #3794 chose allow origin
* feature: #3794 chose allow origin
* feature: #3794 check style
* feature: #3794 shenyu-integrated-test-combination
* feature: #3794 shenyu-integrated-test
* Empty Commit to setup deployments
* feature: #3794 check style
* feature: #3794 check style
* feature: #3794 unit test
* feature: #3794 unit test
* feature: #3794 unit test
---
.../config/properties/WebsocketSyncProperties.java | 21 ++++++++++++++++
.../listener/websocket/WebsocketConfigurator.java | 22 +++++++++++++++++
shenyu-admin/src/main/resources/application.yml | 1 +
.../src/main/resources/application.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application.yml | 1 +
.../src/main/resources/application.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../src/main/resources/application-local.yml | 1 +
.../data/websocket/WebsocketSyncDataService.java | 14 ++++++++++-
.../websocket/client/ShenyuWebsocketClient.java | 26 +++++++++++++++++---
.../data/websocket/config/WebsocketConfig.java | 28 +++++++++++++++++++---
.../data/websocket/config/WebsocketConfigTest.java | 11 ++++++---
18 files changed, 124 insertions(+), 10 deletions(-)
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/WebsocketSyncProperties.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/WebsocketSyncProperties.java
index 20c28cfb6..e5b41302e 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/WebsocketSyncProperties.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/WebsocketSyncProperties.java
@@ -35,6 +35,11 @@ public class WebsocketSyncProperties {
*/
private int messageMaxSize;
+ /**
+ * allowOrigins.
+ */
+ private String allowOrigins;
+
/**
* Gets the value of enabled.
*
@@ -70,4 +75,20 @@ public class WebsocketSyncProperties {
public void setMessageMaxSize(final int messageMaxSize) {
this.messageMaxSize = messageMaxSize;
}
+
+ /**
+ * set allowOrigins.
+ * @return allowOrigins
+ */
+ public String getAllowOrigins() {
+ return allowOrigins;
+ }
+
+ /**
+ * get allowOrigins.
+ * @param allowOrigins allowOrigins
+ */
+ public void setAllowOrigins(final String allowOrigins) {
+ this.allowOrigins = allowOrigins;
+ }
}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketConfigurator.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketConfigurator.java
index faae7ede7..8a94a4506 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketConfigurator.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketConfigurator.java
@@ -17,7 +17,11 @@
package org.apache.shenyu.admin.listener.websocket;
+import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.admin.config.properties.WebsocketSyncProperties;
+import org.apache.shenyu.admin.spring.SpringBeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.ServletContextInitializer;
@@ -42,6 +46,8 @@ import static
org.apache.tomcat.websocket.server.Constants.TEXT_BUFFER_SIZE_SERV
@Configuration
public class WebsocketConfigurator extends ServerEndpointConfig.Configurator
implements ServletContextInitializer {
+ private static final Logger LOG =
LoggerFactory.getLogger(WebsocketConfigurator.class);
+
@Autowired
private WebsocketSyncProperties websocketSyncProperties;
@@ -52,6 +58,22 @@ public class WebsocketConfigurator extends
ServerEndpointConfig.Configurator imp
super.modifyHandshake(sec, request, response);
}
+ @Override
+ public boolean checkOrigin(final String originHeaderValue) {
+ final WebsocketSyncProperties bean =
SpringBeanUtils.getInstance().getBean(WebsocketSyncProperties.class);
+ if (StringUtils.isNotEmpty(bean.getAllowOrigins())) {
+ String[] split = StringUtils.split(bean.getAllowOrigins(), ";");
+ for (String configAllow : split) {
+ if (StringUtils.equals(configAllow, originHeaderValue)) {
+ return true;
+ }
+ }
+ LOG.error("originHeaderValue is forbidden: " + originHeaderValue);
+ return false;
+ }
+ return super.checkOrigin(originHeaderValue);
+ }
+
@Override
public void onStartup(final ServletContext servletContext) throws
ServletException {
int messageMaxSize = websocketSyncProperties.getMessageMaxSize();
diff --git a/shenyu-admin/src/main/resources/application.yml
b/shenyu-admin/src/main/resources/application.yml
index e8d773023..5676bd2f2 100755
--- a/shenyu-admin/src/main/resources/application.yml
+++ b/shenyu-admin/src/main/resources/application.yml
@@ -49,6 +49,7 @@ shenyu:
websocket:
enabled: true
messageMaxSize: 10240
+ allowOrigins: ws://localhost:9095;ws://localhost:9195;
# zookeeper:
# url: localhost:2181
# sessionTimeout: 5000
diff --git a/shenyu-bootstrap/src/main/resources/application.yml
b/shenyu-bootstrap/src/main/resources/application.yml
index 4420234b6..de8954762 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -182,6 +182,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
# zookeeper:
# url: localhost:2181
# sessionTimeout: 5000
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-alibaba-dubbo/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-alibaba-dubbo/src/main/resources/application-local.yml
index 4a775e25c..62566a9c6 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-alibaba-dubbo/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-alibaba-dubbo/src/main/resources/application-local.yml
@@ -40,6 +40,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-apache-dubbo/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-apache-dubbo/src/main/resources/application-local.yml
index b6c11e4d2..8c4167785 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-apache-dubbo/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-apache-dubbo/src/main/resources/application-local.yml
@@ -36,6 +36,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-combination/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-combination/src/main/resources/application-local.yml
index b6c11e4d2..8c4167785 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-combination/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-combination/src/main/resources/application-local.yml
@@ -36,6 +36,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-grpc/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-grpc/src/main/resources/application-local.yml
index b2074ba0a..144b2b454 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-grpc/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-grpc/src/main/resources/application-local.yml
@@ -50,6 +50,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-http/src/main/resources/application.yml
b/shenyu-integrated-test/shenyu-integrated-test-http/src/main/resources/application.yml
index b7d813ba9..8056e24d1 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-http/src/main/resources/application.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-http/src/main/resources/application.yml
@@ -48,6 +48,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
# zookeeper:
# url: localhost:2181
# sessionTimeout: 5000
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-https/src/main/resources/application.yml
b/shenyu-integrated-test/shenyu-integrated-test-https/src/main/resources/application.yml
index 563372e04..57b2e3e8c 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-https/src/main/resources/application.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-https/src/main/resources/application.yml
@@ -44,6 +44,7 @@ shenyu:
sync:
websocket:
urls: ws://shenyu-admin:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-motan/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-motan/src/main/resources/application-local.yml
index b6c11e4d2..8c4167785 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-motan/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-motan/src/main/resources/application-local.yml
@@ -36,6 +36,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-sofa/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-sofa/src/main/resources/application-local.yml
index e26257395..23c175227 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-sofa/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-sofa/src/main/resources/application-local.yml
@@ -36,6 +36,7 @@ shenyu:
sync:
websocket:
urls: ws://shenyu-admin:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-spring-cloud/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-spring-cloud/src/main/resources/application-local.yml
index 8666f83ec..d7fe2ffbb 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-spring-cloud/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-spring-cloud/src/main/resources/application-local.yml
@@ -40,6 +40,7 @@ shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-websocket/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-websocket/src/main/resources/application-local.yml
index 3ad9801f4..6082c1464 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-websocket/src/main/resources/application-local.yml
+++
b/shenyu-integrated-test/shenyu-integrated-test-websocket/src/main/resources/application-local.yml
@@ -36,6 +36,7 @@ shenyu:
sync:
websocket:
urls: ws://shenyu-admin:9095/websocket
+ allowOrigin: ws://localhost:9195
exclude:
enabled: true
paths:
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/WebsocketSyncDataService.java
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/WebsocketSyncDataService.java
index c761bdd80..3dc1df72b 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/WebsocketSyncDataService.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/WebsocketSyncDataService.java
@@ -17,6 +17,7 @@
package org.apache.shenyu.plugin.sync.data.websocket;
+import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import
org.apache.shenyu.plugin.sync.data.websocket.client.ShenyuWebsocketClient;
import org.apache.shenyu.plugin.sync.data.websocket.config.WebsocketConfig;
@@ -31,6 +32,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -42,6 +44,11 @@ public class WebsocketSyncDataService implements
SyncDataService {
* logger.
*/
private static final Logger LOG =
LoggerFactory.getLogger(WebsocketSyncDataService.class);
+
+ /**
+ * see
https://github.com/apache/tomcat/blob/main/java/org/apache/tomcat/websocket/Constants.java#L99.
+ */
+ private static final String ORIGIN_HEADER_NAME = "Origin";
private final List<ShenyuWebsocketClient> clients = new ArrayList<>();
@@ -60,7 +67,12 @@ public class WebsocketSyncDataService implements
SyncDataService {
String[] urls = StringUtils.split(websocketConfig.getUrls(), ",");
for (String url : urls) {
try {
- clients.add(new ShenyuWebsocketClient(new URI(url),
Objects.requireNonNull(pluginDataSubscriber), metaDataSubscribers,
authDataSubscribers));
+ if (StringUtils.isNotEmpty(websocketConfig.getAllowOrigin())) {
+ Map<String, String> headers =
ImmutableMap.of(ORIGIN_HEADER_NAME, websocketConfig.getAllowOrigin());
+ clients.add(new ShenyuWebsocketClient(new URI(url),
headers, Objects.requireNonNull(pluginDataSubscriber), metaDataSubscribers,
authDataSubscribers));
+ } else {
+ clients.add(new ShenyuWebsocketClient(new URI(url),
Objects.requireNonNull(pluginDataSubscriber), metaDataSubscribers,
authDataSubscribers));
+ }
} catch (URISyntaxException e) {
LOG.error("websocket url({}) is error", url, e);
}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/client/ShenyuWebsocketClient.java
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/client/ShenyuWebsocketClient.java
index cbcb536fd..dc92ec9e4 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/client/ShenyuWebsocketClient.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/client/ShenyuWebsocketClient.java
@@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -73,7 +74,25 @@ public final class ShenyuWebsocketClient extends
WebSocketClient {
this.timer = WheelTimerFactory.getSharedTimer();
this.connection();
}
-
+
+ /**
+ * Instantiates a new shenyu websocket client.
+ * @param serverUri the server uri
+ * @param headers the headers
+ * @param pluginDataSubscriber the plugin data subscriber
+ * @param metaDataSubscribers the meta data subscribers
+ * @param authDataSubscribers the auth data subscribers
+ */
+ public ShenyuWebsocketClient(final URI serverUri, final Map<String,
String> headers,
+ final PluginDataSubscriber
pluginDataSubscriber,
+ final List<MetaDataSubscriber>
metaDataSubscribers,
+ final List<AuthDataSubscriber>
authDataSubscribers) {
+ super(serverUri, headers);
+ this.websocketDataHandler = new
WebsocketDataHandler(pluginDataSubscriber, metaDataSubscribers,
authDataSubscribers);
+ this.timer = WheelTimerFactory.getSharedTimer();
+ this.connection();
+ }
+
private void connection() {
this.connectBlocking();
this.timer.add(timerTask = new AbstractRoundTask(null,
TimeUnit.SECONDS.toMillis(10)) {
@@ -89,7 +108,8 @@ public final class ShenyuWebsocketClient extends
WebSocketClient {
boolean success = false;
try {
success = super.connectBlocking();
- } catch (Exception ignored) {
+ } catch (Exception exception) {
+ LOG.error("websocket connection server[{}] is error.....[{}]",
this.getURI().toString(), exception.getMessage());
}
if (success) {
LOG.info("websocket connection server[{}] is successful.....",
this.getURI().toString());
@@ -98,7 +118,7 @@ public final class ShenyuWebsocketClient extends
WebSocketClient {
}
return success;
}
-
+
@Override
public void onOpen(final ServerHandshake serverHandshake) {
if (!alreadySync) {
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfig.java
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfig.java
index 936a6982b..eff0f2ed5 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfig.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/main/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfig.java
@@ -27,6 +27,11 @@ public class WebsocketConfig {
*/
private String urls;
+ /**
+ * allowOrigin.
+ */
+ private String allowOrigin;
+
/**
* get urls.
*
@@ -45,6 +50,22 @@ public class WebsocketConfig {
this.urls = urls;
}
+ /**
+ * get allowOrigin.
+ * @return allowOrigin
+ */
+ public String getAllowOrigin() {
+ return allowOrigin;
+ }
+
+ /**
+ * set allowOrigin.
+ * @param allowOrigin allowOrigin
+ */
+ public void setAllowOrigin(final String allowOrigin) {
+ this.allowOrigin = allowOrigin;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -54,12 +75,12 @@ public class WebsocketConfig {
return false;
}
WebsocketConfig that = (WebsocketConfig) o;
- return Objects.equals(urls, that.urls);
+ return Objects.equals(urls, that.urls) && Objects.equals(allowOrigin,
that.allowOrigin);
}
@Override
public int hashCode() {
- return Objects.hash(urls);
+ return Objects.hash(urls, allowOrigin);
}
@Override
@@ -67,7 +88,8 @@ public class WebsocketConfig {
return "WebsocketConfig{"
+ "urls='"
+ urls
- + '\''
+ + ", allowOrigin='"
+ + allowOrigin
+ '}';
}
}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/test/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfigTest.java
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/test/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfigTest.java
index 89b1bafb2..54d10314d 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-websocket/src/test/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfigTest.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-websocket/src/test/java/org/apache/shenyu/plugin/sync/data/websocket/config/WebsocketConfigTest.java
@@ -32,23 +32,28 @@ public class WebsocketConfigTest {
private static final String URLS = "ws://localhost:9095/websocket";
+ private static final String ALLOW_ORIGIN = "ws://localhost:9095";
+
private WebsocketConfig websocketConfig;
@BeforeEach
public void setUp() {
websocketConfig = new WebsocketConfig();
websocketConfig.setUrls(URLS);
+ websocketConfig.setAllowOrigin(ALLOW_ORIGIN);
}
@Test
public void testGetterSetter() {
assertEquals(URLS, websocketConfig.getUrls());
+ assertEquals(ALLOW_ORIGIN, websocketConfig.getAllowOrigin());
}
@Test
public void testEquals() {
WebsocketConfig that = new WebsocketConfig();
that.setUrls(URLS);
+ that.setAllowOrigin(ALLOW_ORIGIN);
assertEquals(websocketConfig, websocketConfig);
assertEquals(websocketConfig, that);
assertNotEquals(websocketConfig, null);
@@ -57,13 +62,13 @@ public class WebsocketConfigTest {
@Test
public void testHashCode() {
- assertEquals(Objects.hash(websocketConfig.getUrls()),
websocketConfig.hashCode());
+ assertEquals(Objects.hash(websocketConfig.getUrls(),
websocketConfig.getAllowOrigin()), websocketConfig.hashCode());
}
@Test
public void testToString() {
- String[] array = {"WebsocketConfig{urls='", "'}"};
- String expected = String.join(websocketConfig.getUrls(), array);
+ String toString = "WebsocketConfig{urls='%s, allowOrigin='%s}";
+ String expected = String.format(toString, URLS, ALLOW_ORIGIN);
assertEquals(expected, websocketConfig.toString());
}
}