From 3d64f1192dee1a96e166609776cdb50e9ddd923b Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Tue, 10 Jan 2023 14:13:12 +0800 Subject: [PATCH 1/6] Update protobuf-java version in both dependency and plugin (#11261) --- dubbo-demo/dubbo-demo-triple/pom.xml | 4 +++- dubbo-dependencies-bom/pom.xml | 2 +- dubbo-rpc/dubbo-rpc-triple/pom.xml | 4 +++- dubbo-xds/pom.xml | 16 ++++++++++++++-- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/dubbo-demo/dubbo-demo-triple/pom.xml b/dubbo-demo/dubbo-demo-triple/pom.xml index 6597e0c011..63979e1494 100644 --- a/dubbo-demo/dubbo-demo-triple/pom.xml +++ b/dubbo-demo/dubbo-demo-triple/pom.xml @@ -34,6 +34,7 @@ true 1.8 1.8 + 3.18.3 3.7.0 @@ -118,6 +119,7 @@ com.google.protobuf protobuf-java + ${protobuf-java.version} @@ -141,7 +143,7 @@ protobuf-maven-plugin 0.6.1 - com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} triple-java build/generated/source/proto/main/java diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index cf8eb3088d..d726d7a7b7 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -112,7 +112,7 @@ 3.1.15 0.15.0 4.0.38 - 3.16.3 + 3.18.3 1.3.2 3.1.0 9.4.43.v20210629 diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml index eeca0e6937..d024a20a5e 100644 --- a/dubbo-rpc/dubbo-rpc-triple/pom.xml +++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml @@ -32,6 +32,7 @@ 0.0.4.1-SNAPSHOT 1.0.4 3.4.19 + 3.18.3 @@ -52,6 +53,7 @@ com.google.protobuf protobuf-java + ${protobuf-java.version} org.springframework @@ -112,7 +114,7 @@ 0.6.1 - com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} diff --git a/dubbo-xds/pom.xml b/dubbo-xds/pom.xml index d162c2e163..913fe84187 100644 --- a/dubbo-xds/pom.xml +++ b/dubbo-xds/pom.xml @@ -31,6 +31,8 @@ The xDS Integration false + 3.18.3 + 1.41.0 @@ -50,16 +52,19 @@ io.grpc grpc-protobuf + ${grpc.version} io.grpc grpc-stub + ${grpc.version} io.grpc grpc-netty-shaded + ${grpc.version} @@ -67,9 +72,16 @@ api + + com.google.protobuf + protobuf-java + ${protobuf-java.version} + + com.google.protobuf protobuf-java-util + ${protobuf-java.version} @@ -101,9 +113,9 @@ protobuf-maven-plugin 0.6.1 - com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.31.1:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} From 213363422e0981b1f227af61a01876b8adcadfda Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Wed, 11 Jan 2023 10:47:37 +0800 Subject: [PATCH 2/6] Add mapping retry (#11265) --- .../dubbo/config/ApplicationConfig.java | 29 ++++-- .../apache/dubbo/config/ServiceConfig.java | 64 ++++++++----- .../dubbo/config/ServiceConfigTest.java | 94 ++++++++++++++++++- .../src/main/resources/META-INF/dubbo.xsd | 5 + 4 files changed, 158 insertions(+), 34 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java index 33852d430a..3eb3b50b14 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java @@ -16,6 +16,14 @@ */ package org.apache.dubbo.config; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.dubbo.common.compiler.support.AdaptiveCompiler; import org.apache.dubbo.common.infra.InfraAdapter; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; @@ -25,14 +33,6 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_PROTOCOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_VERSION_KEY; @@ -198,6 +198,11 @@ public class ApplicationConfig extends AbstractConfig { */ private Integer metadataServicePort; + /** + * The retry interval of service name mapping + */ + private Integer mappingRetryInterval; + /** * used to set extensions of probe in qos */ @@ -564,6 +569,14 @@ public class ApplicationConfig extends AbstractConfig { this.metadataServicePort = metadataServicePort; } + public Integer getMappingRetryInterval() { + return mappingRetryInterval; + } + + public void setMappingRetryInterval(Integer mappingRetryInterval) { + this.mappingRetryInterval = mappingRetryInterval; + } + @Parameter(key = METADATA_SERVICE_PROTOCOL_KEY) public String getMetadataServiceProtocol() { return metadataServiceProtocol; diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index f4e087df2c..4fb4056d75 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -16,6 +16,18 @@ */ package org.apache.dubbo.config; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.Version; @@ -24,6 +36,7 @@ import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; +import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.CollectionUtils; @@ -49,17 +62,6 @@ import org.apache.dubbo.rpc.model.ScopeModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; import org.apache.dubbo.rpc.service.GenericService; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR; @@ -261,21 +263,41 @@ public class ServiceConfig extends ServiceConfigBase { exportedURLs.forEach(url -> { if (url.getParameters().containsKey(SERVICE_NAME_MAPPING_KEY)) { ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(getScopeModel()); - try { - boolean succeeded = serviceNameMapping.map(url); - if (succeeded) { - logger.info("Successfully registered interface application mapping for service " + url.getServiceKey()); - } else { - logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey()); - } - } catch (Exception e) { - logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey(), e); - } + ScheduledExecutorService scheduledExecutor = getScopeModel().getBeanFactory() + .getBean(FrameworkExecutorRepository.class).getSharedScheduledExecutor(); + mapServiceName(url, serviceNameMapping, scheduledExecutor); } }); onExported(); } + protected void mapServiceName(URL url, ServiceNameMapping serviceNameMapping, ScheduledExecutorService scheduledExecutor) { + if (!exported) { + return; + } + logger.info("Try to register interface application mapping for service " + url.getServiceKey()); + boolean succeeded = false; + try { + succeeded = serviceNameMapping.map(url); + if (succeeded) { + logger.info("Successfully registered interface application mapping for service " + url.getServiceKey()); + } else { + logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey()); + } + } catch (Exception e) { + logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey(), e); + } + if (!succeeded) { + scheduleToMapping(scheduledExecutor, serviceNameMapping, url); + } + } + + private void scheduleToMapping(ScheduledExecutorService scheduledExecutor, ServiceNameMapping serviceNameMapping, URL url) { + Integer mappingRetryInterval = getApplication().getMappingRetryInterval(); + scheduledExecutor.schedule(() -> mapServiceName(url, serviceNameMapping, scheduledExecutor), + mappingRetryInterval == null ? 5000 : mappingRetryInterval, TimeUnit.MILLISECONDS); + } + private void checkAndUpdateSubConfigs() { // Use default configs defined explicitly with global scope diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java index 36acb7ed91..0e676ba427 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java @@ -17,6 +17,14 @@ package org.apache.dubbo.config; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicInteger; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.api.DemoService; @@ -27,22 +35,22 @@ import org.apache.dubbo.config.mock.MockRegistryFactory2; import org.apache.dubbo.config.mock.MockServiceListener; import org.apache.dubbo.config.mock.TestProxyFactory; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; +import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.metadata.ServiceNameMapping; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.service.GenericService; - -import com.google.common.collect.Lists; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.CountDownLatch; +import com.google.common.collect.Lists; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; @@ -59,6 +67,7 @@ import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; +import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -529,4 +538,79 @@ class ServiceConfigTest { service.export(); }); } + + @Test + void testMappingRetry() { + FrameworkModel frameworkModel = new FrameworkModel(); + ApplicationModel applicationModel = frameworkModel.newApplication(); + ServiceConfig serviceConfig = new ServiceConfig<>(applicationModel.newModule()); + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); + AtomicInteger count = new AtomicInteger(0); + ServiceNameMapping serviceNameMapping = new ServiceNameMapping() { + @Override + public boolean map(URL url) { + if (count.incrementAndGet() < 5) { + throw new RuntimeException(); + } + return count.get() > 10; + } + + @Override + public void initInterfaceAppMapping(URL subscribedURL) { + + } + + @Override + public Set getAndListen(URL registryURL, URL subscribedURL, MappingListener listener) { + return null; + } + + @Override + public MappingListener stopListen(URL subscribeURL, MappingListener listener) { + return null; + } + + @Override + public void putCachedMapping(String serviceKey, Set apps) { + + } + + @Override + public Set getCachedMapping(String mappingKey) { + return null; + } + + @Override + public Set getCachedMapping(URL consumerURL) { + return null; + } + + @Override + public Set getRemoteMapping(URL consumerURL) { + return null; + } + + @Override + public Map> getCachedMapping() { + return null; + } + + @Override + public Set removeCachedMapping(String serviceKey) { + return null; + } + + @Override + public void $destroy() { + + } + }; + ApplicationConfig applicationConfig = new ApplicationConfig("app"); + applicationConfig.setMappingRetryInterval(10); + serviceConfig.setApplication(applicationConfig); + serviceConfig.mapServiceName(URL.valueOf(""), serviceNameMapping, scheduledExecutorService); + + await().until(() -> count.get() > 10); + scheduledExecutorService.shutdown(); + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd index c840300cf8..7b6e60e9e7 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd @@ -456,6 +456,11 @@ + + + + + From 671c7ff9506982efa2054b3b8de0f97940bb1af5 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Wed, 11 Jan 2023 11:04:52 +0800 Subject: [PATCH 3/6] Enhance nacos regsitry (#11262) --- .../nacos/NacosNamingServiceWrapper.java | 102 ++++++- .../registry/nacos/NacosServiceDiscovery.java | 29 ++ .../nacos/function/NacosConsumer.java | 40 +++ .../nacos/function/NacosFunction.java | 41 +++ .../nacos/util/NacosNamingServiceUtils.java | 18 +- .../registry/nacos/MockNamingService.java | 289 ++++++++++++++++++ .../nacos/NacosNamingServiceWrapperTest.java | 116 +++++++ .../registry/nacos/NacosRegistryTest.java | 30 +- 8 files changed, 632 insertions(+), 33 deletions(-) create mode 100644 dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosConsumer.java create mode 100644 dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosFunction.java create mode 100644 dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/MockNamingService.java create mode 100644 dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapperTest.java diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java index de4dc69480..e1185aa15b 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java @@ -16,7 +16,14 @@ */ package org.apache.dubbo.registry.nacos; +import java.util.List; + +import org.apache.dubbo.common.constants.LoggerCodeConstants; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.registry.nacos.function.NacosConsumer; +import org.apache.dubbo.registry.nacos.function.NacosFunction; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; @@ -24,9 +31,8 @@ import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; -import java.util.List; - public class NacosNamingServiceWrapper { + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceWrapper.class); private static final String INNERCLASS_SYMBOL = "$"; @@ -34,8 +40,14 @@ public class NacosNamingServiceWrapper { private final NamingService namingService; - public NacosNamingServiceWrapper(NamingService namingService) { + private final int retryTimes; + + private final int sleepMsBetweenRetries; + + public NacosNamingServiceWrapper(NamingService namingService, int retryTimes, int sleepMsBetweenRetries) { this.namingService = namingService; + this.retryTimes = Math.max(retryTimes, 0); + this.sleepMsBetweenRetries = sleepMsBetweenRetries; } @@ -44,36 +56,36 @@ public class NacosNamingServiceWrapper { } public void subscribe(String serviceName, String group, EventListener eventListener) throws NacosException { - namingService.subscribe(handleInnerSymbol(serviceName), group, eventListener); + accept(naming -> naming.subscribe(handleInnerSymbol(serviceName), group, eventListener)); } public void unsubscribe(String serviceName, String group, EventListener eventListener) throws NacosException { - namingService.unsubscribe(handleInnerSymbol(serviceName), group, eventListener); + accept(naming -> naming.unsubscribe(handleInnerSymbol(serviceName), group, eventListener)); } public List getAllInstances(String serviceName, String group) throws NacosException { - return namingService.getAllInstances(handleInnerSymbol(serviceName), group); + return apply(naming -> naming.getAllInstances(handleInnerSymbol(serviceName), group)); } public void registerInstance(String serviceName, String group, Instance instance) throws NacosException { - namingService.registerInstance(handleInnerSymbol(serviceName), group, instance); + accept(naming -> naming.registerInstance(handleInnerSymbol(serviceName), group, instance)); } public void deregisterInstance(String serviceName, String group, String ip, int port) throws NacosException { - namingService.deregisterInstance(handleInnerSymbol(serviceName), group, ip, port); + accept(naming -> naming.deregisterInstance(handleInnerSymbol(serviceName), group, ip, port)); } public void deregisterInstance(String serviceName, String group, Instance instance) throws NacosException { - namingService.deregisterInstance(handleInnerSymbol(serviceName), group, instance); + accept(naming -> naming.deregisterInstance(handleInnerSymbol(serviceName), group, instance)); } public ListView getServicesOfServer(int pageNo, int pageSize, String group) throws NacosException { - return namingService.getServicesOfServer(pageNo, pageSize, group); + return apply(naming -> naming.getServicesOfServer(pageNo, pageSize, group)); } public List selectInstances(String serviceName, String group, boolean healthy) throws NacosException { - return namingService.selectInstances(handleInnerSymbol(serviceName), group, healthy); + return apply(naming -> naming.selectInstances(handleInnerSymbol(serviceName), group, healthy)); } public void shutdown() throws NacosException { @@ -90,4 +102,72 @@ public class NacosNamingServiceWrapper { } return serviceName.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL); } + + private R apply(NacosFunction command) throws NacosException { + NacosException le = null; + R result = null; + int times = 0; + for (; times < retryTimes + 1; times++) { + try { + result = command.apply(namingService); + le = null; + break; + } catch (NacosException e) { + le = e; + logger.warn(LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", + "Failed to request nacos naming server. " + + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + + "Try times: " + times + 1, e); + if (times < retryTimes) { + try { + Thread.sleep(sleepMsBetweenRetries); + } catch (InterruptedException ex) { + logger.warn(LoggerCodeConstants.INTERNAL_INTERRUPTED, "", "", "Interrupted when waiting to retry.", ex); + Thread.currentThread().interrupt(); + } + } + } + } + if (le != null) { + throw le; + } + if (times > 1) { + logger.info("Failed to request nacos naming server for " + (times - 1) + " times and finally success. " + + "This may caused by high stress of nacos server."); + } + return result; + } + + private void accept(NacosConsumer command) throws NacosException { + NacosException le = null; + int times = 0; + for (; times < retryTimes + 1; times++) { + try { + command.accept(namingService); + le = null; + break; + } catch (NacosException e) { + le = e; + logger.warn(LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION, "", "", + "Failed to request nacos naming server. " + + (times < retryTimes ? "Dubbo will try to retry in " + sleepMsBetweenRetries + ". " : "Exceed retry max times.") + + "Try times: " + times + 1, e); + if (times < retryTimes) { + try { + Thread.sleep(sleepMsBetweenRetries); + } catch (InterruptedException ex) { + logger.warn(LoggerCodeConstants.INTERNAL_INTERRUPTED, "", "", "Interrupted when waiting to retry.", ex); + Thread.currentThread().interrupt(); + } + } + } + } + if (le != null) { + throw le; + } + if (times > 1) { + logger.info("Failed to request nacos naming server for " + (times - 1) + " times and finally success. " + + "This may caused by high stress of nacos server."); + } + } } diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java index f2b3c2d4ba..28c2efd44e 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java @@ -18,6 +18,7 @@ package org.apache.dubbo.registry.nacos; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -34,6 +35,7 @@ import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; +import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.model.ApplicationModel; import com.alibaba.nacos.api.exception.NacosException; @@ -46,9 +48,12 @@ import com.alibaba.nacos.api.naming.pojo.ListView; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NACOS_EXCEPTION; import static org.apache.dubbo.common.function.ThrowableConsumer.execute; +import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; +import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.createNamingService; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.getGroup; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.toInstance; +import static org.apache.dubbo.rpc.RpcException.REGISTRY_EXCEPTION; /** * Nacos {@link ServiceDiscovery} implementation @@ -98,6 +103,30 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { }); } + @Override + protected void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance) throws RuntimeException { + if (EMPTY_REVISION.equals(getExportedServicesRevision(newServiceInstance))) { + super.doUpdate(oldServiceInstance, newServiceInstance); + return; + } + + if (!Objects.equals(oldServiceInstance.getServiceName(), newServiceInstance.getServiceName()) || + !Objects.equals(oldServiceInstance.getAddress(), newServiceInstance.getAddress()) || + !Objects.equals(oldServiceInstance.getPort(), newServiceInstance.getPort())) { + // ignore if host-ip changed + super.doUpdate(oldServiceInstance, newServiceInstance); + return; + } + + try { + this.serviceInstance = newServiceInstance; + // override without unregister + this.doRegister(newServiceInstance); + } catch (Exception e) { + throw new RpcException(REGISTRY_EXCEPTION, "Failed register instance " + newServiceInstance.toString(), e); + } + } + @Override public Set getServices() { return ThrowableFunction.execute(namingService, service -> { diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosConsumer.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosConsumer.java new file mode 100644 index 0000000000..1fd3557426 --- /dev/null +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosConsumer.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.nacos.function; + +import java.util.function.Function; + +import com.alibaba.nacos.api.exception.NacosException; + +/** + * A function interface for action with {@link NacosException} + * + * @see Function + * @see NacosException + * @since 3.1.5 + */ +@FunctionalInterface +public interface NacosConsumer { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @throws NacosException if met with any error + */ + void accept(T t) throws NacosException; +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosFunction.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosFunction.java new file mode 100644 index 0000000000..1d0050b75f --- /dev/null +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/function/NacosFunction.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.nacos.function; + +import java.util.function.Function; + +import com.alibaba.nacos.api.exception.NacosException; + +/** + * A function interface for action with {@link NacosException} + * + * @see Function + * @see NacosException + * @since 3.1.5 + */ +@FunctionalInterface +public interface NacosFunction { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + * @throws NacosException if met with any error + */ + R apply(T t) throws NacosException; +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java index 08f046a76b..c085701d86 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java @@ -16,6 +16,9 @@ */ package org.apache.dubbo.registry.nacos.util; +import java.util.Map; +import java.util.Properties; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -32,10 +35,6 @@ import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; -import java.util.Map; -import java.util.Properties; - -import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_LOAD_CACHE_AT_START; import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; @@ -56,6 +55,11 @@ public class NacosNamingServiceUtils { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceUtils.class); private static final String NACOS_GROUP_KEY = "nacos.group"; + private static final String NACOS_RETRY_KEY = "nacos.retry"; + + private static final String NACOS_RETRY_WAIT_KEY = "nacos.retry-wait"; + + /** * Convert the {@link ServiceInstance} to {@link Instance} * @@ -124,7 +128,9 @@ public class NacosNamingServiceUtils { } throw new IllegalStateException(e); } - return new NacosNamingServiceWrapper(namingService); + int retryTimes = connectionURL.getParameter(NACOS_RETRY_KEY, 10); + int sleepMsBetweenRetries = connectionURL.getParameter(NACOS_RETRY_WAIT_KEY, 10); + return new NacosNamingServiceWrapper(namingService, retryTimes, sleepMsBetweenRetries); } private static Properties buildNacosProperties(URL url) { @@ -164,8 +170,6 @@ public class NacosNamingServiceUtils { if (StringUtils.isNotEmpty(url.getPassword())) { properties.put(PASSWORD, url.getPassword()); } - - putPropertyIfAbsent(url, properties, NAMING_LOAD_CACHE_AT_START, "true"); } private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) { diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/MockNamingService.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/MockNamingService.java new file mode 100644 index 0000000000..e6e7896908 --- /dev/null +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/MockNamingService.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.nacos; + +import java.util.List; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.selector.AbstractSelector; + +public class MockNamingService implements NamingService { + @Override + public void registerInstance(String serviceName, String ip, int port) throws NacosException { + + } + + @Override + public void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException { + + } + + @Override + public void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { + + } + + @Override + public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { + + } + + @Override + public void registerInstance(String serviceName, Instance instance) throws NacosException { + + } + + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + + } + + @Override + public void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, Instance instance) throws NacosException { + + } + + @Override + public void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException { + + } + + @Override + public List getAllInstances(String serviceName) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, String groupName) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, List clusters) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, boolean healthy) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException { + return null; + } + + @Override + public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException { + return null; + } + + @Override + public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { + return null; + } + + @Override + public void subscribe(String serviceName, EventListener listener) throws NacosException { + + } + + @Override + public void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException { + + } + + @Override + public void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException { + + } + + @Override + public void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException { + + } + + @Override + public void unsubscribe(String serviceName, EventListener listener) throws NacosException { + + } + + @Override + public void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException { + + } + + @Override + public void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException { + + } + + @Override + public void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException { + + } + + @Override + public ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException { + return null; + } + + @Override + public ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException { + return null; + } + + @Override + public ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException { + return null; + } + + @Override + public ListView getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { + return null; + } + + @Override + public List getSubscribeServices() throws NacosException { + return null; + } + + @Override + public String getServerStatus() { + return null; + } + + @Override + public void shutDown() throws NacosException { + + } +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapperTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapperTest.java new file mode 100644 index 0000000000..f348255d7e --- /dev/null +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapperTest.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.nacos; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; + +class NacosNamingServiceWrapperTest { + + @Test + void testSuccess() { + NamingService namingService = new MockNamingService() { + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + + } + + @Override + public List getAllInstances(String serviceName, String groupName) throws NacosException { + return null; + } + }; + + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 0, 0); + try { + nacosNamingServiceWrapper.registerInstance("Test", "Test", null); + } catch (NacosException e) { + Assertions.fail(e); + } + try { + nacosNamingServiceWrapper.getAllInstances("Test", "Test"); + } catch (NacosException e) { + Assertions.fail(e); + } + } + + @Test + void testFailNoRetry() { + NamingService namingService = new MockNamingService() { + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + throw new NacosException(); + } + + @Override + public List getAllInstances(String serviceName, String groupName) throws NacosException { + throw new NacosException(); + } + }; + + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 0, 0); + Assertions.assertThrows(NacosException.class, () -> nacosNamingServiceWrapper.registerInstance("Test", "Test", null)); + Assertions.assertThrows(NacosException.class, () -> nacosNamingServiceWrapper.getAllInstances("Test", "Test")); + } + + + @Test + void testFailRetry() { + NamingService namingService = new MockNamingService() { + private final AtomicInteger count1 = new AtomicInteger(0); + private final AtomicInteger count2 = new AtomicInteger(0); + + @Override + public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { + if (count1.incrementAndGet() < 10) { + throw new NacosException(); + } + } + + @Override + public List getAllInstances(String serviceName, String groupName) throws NacosException { + if (count2.incrementAndGet() < 10) { + throw new NacosException(); + } + return null; + } + }; + + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 5, 10); + Assertions.assertThrows(NacosException.class, () -> nacosNamingServiceWrapper.registerInstance("Test", "Test", null)); + try { + nacosNamingServiceWrapper.registerInstance("Test", "Test", null); + } catch (NacosException e) { + Assertions.fail(e); + } + + Assertions.assertThrows(NacosException.class, () -> nacosNamingServiceWrapper.getAllInstances("Test", "Test")); + try { + nacosNamingServiceWrapper.getAllInstances("Test", "Test"); + } catch (NacosException e) { + Assertions.fail(e); + } + + } +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java index b5c9ee55ff..c0568d3c52 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java +++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosRegistryTest.java @@ -16,9 +16,19 @@ */ package org.apache.dubbo.registry.nacos; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.NotifyListener; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; @@ -26,16 +36,6 @@ import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.client.naming.NacosNamingService; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; @@ -103,7 +103,7 @@ class NacosRegistryTest { // ignore } - NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService); + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); Set registered; @@ -143,7 +143,7 @@ class NacosRegistryTest { // ignore } - NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService); + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); nacosRegistry.register(serviceUrl); @@ -183,7 +183,7 @@ class NacosRegistryTest { // ignore } - NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService); + NacosNamingServiceWrapper nacosNamingServiceWrapper = new NacosNamingServiceWrapper(namingService, 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); NotifyListener listener = mock(NotifyListener.class); @@ -222,7 +222,7 @@ class NacosRegistryTest { } NacosNamingServiceWrapper nacosNamingServiceWrapper = new - NacosNamingServiceWrapper(namingService); + NacosNamingServiceWrapper(namingService, 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); NotifyListener listener = mock(NotifyListener.class); @@ -276,7 +276,7 @@ class NacosRegistryTest { } NacosNamingServiceWrapper nacosNamingServiceWrapper = new - NacosNamingServiceWrapper(namingService); + NacosNamingServiceWrapper(namingService, 0, 0); nacosRegistry = new NacosRegistry(this.registryUrl, nacosNamingServiceWrapper); Set registered; From 84c1c35aaeca5a917e49c1184d19435416c11a78 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Wed, 11 Jan 2023 11:41:52 +0800 Subject: [PATCH 4/6] Add serialization check (#11217) --- .../common/CommonScopeModelInitializer.java | 2 + .../common/constants/CommonConstants.java | 2 + .../utils/AllowClassNotifyListener.java | 26 +++ .../utils/ClassLoaderResourceLoader.java | 4 +- .../common/utils/SerializeCheckStatus.java | 23 ++ .../common/utils/SerializeClassChecker.java | 30 ++- .../utils/SerializeSecurityManager.java | 220 ++++++++++++++++++ .../resources/security/serialize.allowlist | 125 ++++++++++ .../resources/security/serialize.blockedlist | 74 ++++-- .../src/test/java/com/pojo/Demo1.java | 29 +++ .../src/test/java/com/pojo/Demo2.java | 20 ++ .../src/test/java/com/pojo/Demo3.java | 20 ++ .../src/test/java/com/pojo/Demo4.java | 20 ++ .../src/test/java/com/pojo/Demo5.java | 20 ++ .../src/test/java/com/pojo/Demo6.java | 20 ++ .../src/test/java/com/pojo/Demo7.java | 20 ++ .../src/test/java/com/pojo/Demo8.java | 20 ++ .../test/java/com/pojo/DemoException1.java | 20 ++ .../test/java/com/pojo/DemoException2.java | 20 ++ .../test/java/com/pojo/DemoException3.java | 20 ++ .../src/test/java/com/pojo/Simple.java | 20 ++ .../test/java/com/service/DemoService1.java | 52 +++++ .../test/java/com/service/DemoService2.java | 20 ++ .../utils/SerializeSecurityManagerTest.java | 128 ++++++++++ .../utils/TestAllowClassNotifyListener.java | 20 +- .../resources/security/serialize.allowlist | 20 ++ .../dubbo/config/ReferenceConfigTest.java | 3 +- .../resources/security/serialize.allowlist | 19 ++ dubbo-dependencies-bom/pom.xml | 1 + dubbo-distribution/dubbo-all/pom.xml | 6 - dubbo-distribution/dubbo-core-spi/pom.xml | 6 - .../META-INF/native-image/reflect-config.json | 13 -- .../native-image/resource-config.json | 3 - .../rpc/protocol/ProtocolSecurityWrapper.java | 91 ++++++++ .../internal/org.apache.dubbo.rpc.Protocol | 1 + .../fastjson2/FastJson2ObjectInput.java | 12 +- .../fastjson2/FastJson2ObjectOutput.java | 1 + .../fastjson2/FastJson2Serialization.java | 5 +- .../Fastjson2ScopeModelInitializer.java | 2 + .../fastjson2/Fastjson2SecurityManager.java | 114 +++++++++ .../hessian2/Hessian2AllowClassManager.java | 117 ++++++++++ .../hessian2/Hessian2ClassLoaderListener.java | 33 +++ .../hessian2/Hessian2FactoryManager.java | 110 +++++++++ .../hessian2/Hessian2ObjectInput.java | 21 +- .../hessian2/Hessian2ObjectOutput.java | 11 +- .../Hessian2ScopeModelInitializer.java | 41 ++++ .../hessian2/Hessian2Serialization.java | 11 +- .../hessian2/Hessian2SerializerFactory.java | 9 +- .../AbstractHessian2FactoryInitializer.java | 56 ----- .../dubbo/Hessian2FactoryInitializer.java | 45 ---- .../WhitelistHessian2FactoryInitializer.java | 52 ----- ....hessian2.dubbo.Hessian2FactoryInitializer | 2 - ...ache.dubbo.rpc.model.ScopeModelInitializer | 1 + pom.xml | 1 + 54 files changed, 1539 insertions(+), 243 deletions(-) create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeCheckStatus.java create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java create mode 100644 dubbo-common/src/main/resources/security/serialize.allowlist create mode 100644 dubbo-common/src/test/java/com/pojo/Demo1.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo2.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo3.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo4.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo5.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo6.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo7.java create mode 100644 dubbo-common/src/test/java/com/pojo/Demo8.java create mode 100644 dubbo-common/src/test/java/com/pojo/DemoException1.java create mode 100644 dubbo-common/src/test/java/com/pojo/DemoException2.java create mode 100644 dubbo-common/src/test/java/com/pojo/DemoException3.java create mode 100644 dubbo-common/src/test/java/com/pojo/Simple.java create mode 100644 dubbo-common/src/test/java/com/service/DemoService1.java create mode 100644 dubbo-common/src/test/java/com/service/DemoService2.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityManagerTest.java rename dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/DefaultHessian2FactoryInitializer.java => dubbo-common/src/test/java/org/apache/dubbo/common/utils/TestAllowClassNotifyListener.java (53%) create mode 100644 dubbo-common/src/test/resources/security/serialize.allowlist create mode 100644 dubbo-config/dubbo-config-api/src/test/resources/security/serialize.allowlist create mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSecurityWrapper.java create mode 100644 dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java create mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java create mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ClassLoaderListener.java create mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java create mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java delete mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java delete mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/Hessian2FactoryInitializer.java delete mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/WhitelistHessian2FactoryInitializer.java delete mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer create mode 100644 dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/CommonScopeModelInitializer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/CommonScopeModelInitializer.java index 29f242fe4a..b3748db1b8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/CommonScopeModelInitializer.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/CommonScopeModelInitializer.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.convert.ConverterUtil; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService; import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; +import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ModuleModel; @@ -33,6 +34,7 @@ public class CommonScopeModelInitializer implements ScopeModelInitializer { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(FrameworkExecutorRepository.class); beanFactory.registerBean(ConverterUtil.class); + beanFactory.registerBean(SerializeSecurityManager.class); } @Override diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java index 621a341204..a3f0dc2583 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java @@ -429,6 +429,8 @@ public interface CommonConstants { String SERIALIZE_BLOCKED_LIST_FILE_PATH = "security/serialize.blockedlist"; + String SERIALIZE_ALLOW_LIST_FILE_PATH = "security/serialize.allowlist"; + String QOS_LIVE_PROBE_EXTENSION = "dubbo.application.liveness-probe"; String QOS_READY_PROBE_EXTENSION = "dubbo.application.readiness-probe"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java new file mode 100644 index 0000000000..20e37b62ee --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.utils; + +import java.util.Set; + +public interface AllowClassNotifyListener { + + SerializeCheckStatus DEFAULT_STATUS = SerializeCheckStatus.STRICT; + + void notify(SerializeCheckStatus status, Set prefixList); +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoader.java index fa180db2a2..1d542c5c33 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoader.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassLoaderResourceLoader.java @@ -24,11 +24,11 @@ import java.io.IOException; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.net.URL; +import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -46,7 +46,7 @@ public class ClassLoaderResourceLoader { GlobalResourcesRepository.registerGlobalDisposable(() -> destroy()); } - public static Map> loadResources(String fileName, List classLoaders) throws InterruptedException { + public static Map> loadResources(String fileName, Collection classLoaders) throws InterruptedException { Map> resources = new ConcurrentHashMap<>(); CountDownLatch countDownLatch = new CountDownLatch(classLoaders.size()); for (ClassLoader classLoader : classLoaders) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeCheckStatus.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeCheckStatus.java new file mode 100644 index 0000000000..0046168312 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeCheckStatus.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.utils; + +public enum SerializeCheckStatus { + DISABLED, + WARN, + STRICT, +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java index 5b4eb04b38..ed0af8cabb 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java @@ -17,7 +17,6 @@ package org.apache.dubbo.common.utils; import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; -import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -52,10 +51,10 @@ public class SerializeClassChecker { private final AtomicLong counter = new AtomicLong(0); private SerializeClassChecker() { - String openCheckClass = ConfigurationUtils.getProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "true"); + String openCheckClass = System.getProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "true"); OPEN_CHECK_CLASS = Boolean.parseBoolean(openCheckClass); - String blockAllClassExceptAllow = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false"); + String blockAllClassExceptAllow = System.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false"); BLOCK_ALL_CLASS_EXCEPT_ALLOW = Boolean.parseBoolean(blockAllClassExceptAllow); @@ -79,8 +78,8 @@ public class SerializeClassChecker { logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load blocked class list! Will ignore default blocked list.", e); } - String allowedClassList = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT); - String blockedClassList = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT); + String allowedClassList = System.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT); + String blockedClassList = System.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT); if (StringUtils.isNotEmpty(allowedClassList)) { String[] classStrings = allowedClassList.trim().split(","); @@ -120,34 +119,45 @@ public class SerializeClassChecker { * @throws IllegalArgumentException if class is blocked */ public void validateClass(String name) { + validateClass(name, true); + } + + public boolean validateClass(String name, boolean failOnError) { if (!OPEN_CHECK_CLASS) { - return; + return true; } name = name.toLowerCase(Locale.ROOT); if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) { - return; + return true; } if (CACHE == CLASS_BLOCK_LFU_CACHE.get(name)) { - error(name); + if (failOnError) { + error(name); + } + return false; } for (String allowedPrefix : CLASS_DESERIALIZE_ALLOWED_SET) { if (name.startsWith(allowedPrefix)) { CLASS_ALLOW_LFU_CACHE.put(name, CACHE); - return; + return true; } } for (String blockedPrefix : CLASS_DESERIALIZE_BLOCKED_SET) { if (BLOCK_ALL_CLASS_EXCEPT_ALLOW || name.startsWith(blockedPrefix)) { CLASS_BLOCK_LFU_CACHE.put(name, CACHE); - error(name); + if (failOnError) { + error(name); + } + return false; } } CLASS_ALLOW_LFU_CACHE.put(name, CACHE); + return true; } private void error(String name) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java new file mode 100644 index 0000000000..2dd3432765 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.utils; + +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.net.URL; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_ALLOW_LIST_FILE_PATH; + +public class SerializeSecurityManager { + private final Set allowedPrefix = new LinkedHashSet<>(); + + private final static Logger logger = LoggerFactory.getLogger(SerializeSecurityManager.class); + + private final SerializeClassChecker checker = SerializeClassChecker.getInstance(); + + private final Set listeners = new ConcurrentHashSet<>(); + + private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS; + + public SerializeSecurityManager(FrameworkModel frameworkModel) { + try { + Set classLoaders = frameworkModel.getClassLoaders(); + List urls = ClassLoaderResourceLoader.loadResources(SERIALIZE_ALLOW_LIST_FILE_PATH, classLoaders) + .values() + .stream() + .flatMap(Set::stream) + .collect(Collectors.toList()); + for (URL u : urls) { + try { + String[] lines = IOUtils.readLines(u.openStream()); + for (String line : lines) { + line = line.trim(); + if (StringUtils.isEmpty(line) || line.startsWith("#")) { + continue; + } + allowedPrefix.add(line); + } + } catch (IOException e) { + logger.error("Failed to load allow class list! Will ignore allow lis from " + u, e); + } + } + } catch (InterruptedException e) { + logger.error("Failed to load allow class list! Will ignore allow list from configuration.", e); + } + } + + public void registerInterface(Class clazz) { + Set> markedClass = new HashSet<>(); + markedClass.add(clazz); + + addToAllow(clazz.getName()); + + Method[] methodsToExport = clazz.getMethods(); + + for (Method method : methodsToExport) { + Class[] parameterTypes = method.getParameterTypes(); + for (Class parameterType : parameterTypes) { + checkClass(markedClass, parameterType); + } + + Type[] genericParameterTypes = method.getGenericParameterTypes(); + for (Type genericParameterType : genericParameterTypes) { + checkType(markedClass, genericParameterType); + } + + Class returnType = method.getReturnType(); + checkClass(markedClass, returnType); + + Type genericReturnType = method.getGenericReturnType(); + checkType(markedClass, genericReturnType); + + Class[] exceptionTypes = method.getExceptionTypes(); + for (Class exceptionType : exceptionTypes) { + checkClass(markedClass, exceptionType); + } + + Type[] genericExceptionTypes = method.getGenericExceptionTypes(); + for (Type genericExceptionType : genericExceptionTypes) { + checkType(markedClass, genericExceptionType); + } + } + } + + private void checkType(Set> markedClass, Type type) { + if (type instanceof Class) { + checkClass(markedClass, (Class) type); + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + checkClass(markedClass, (Class) parameterizedType.getRawType()); + for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) { + checkType(markedClass, actualTypeArgument); + } + } else if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + checkType(markedClass, genericArrayType.getGenericComponentType()); + } else if (type instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable) type; + for (Type bound : typeVariable.getBounds()) { + checkType(markedClass, bound); + } + } else if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + for (Type bound : wildcardType.getUpperBounds()) { + checkType(markedClass, bound); + } + for (Type bound : wildcardType.getLowerBounds()) { + checkType(markedClass, bound); + } + } + } + + private void checkClass(Set> markedClass, Class clazz) { + if (markedClass.contains(clazz)) { + return; + } + + markedClass.add(clazz); + + addToAllow(clazz.getName()); + + Class[] interfaces = clazz.getInterfaces(); + for (Class interfaceClass : interfaces) { + checkClass(markedClass, interfaceClass); + } + + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + checkClass(markedClass, superclass); + } + + Field[] fields = clazz.getDeclaredFields(); + + for (Field field : fields) { + if (Modifier.isTransient(field.getModifiers())) { + continue; + } + + Class fieldClass = field.getType(); + checkClass(markedClass, fieldClass); + checkType(markedClass, field.getGenericType()); + } + } + + protected void addToAllow(String className) { + if (!checker.validateClass(className, false)) { + return; + } + + boolean modified; + + // ignore jdk + if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("com.sun.") || + className.startsWith("sun.") || className.startsWith("jdk.")) { + modified = allowedPrefix.add(className); + if (modified) { + notifyListeners(); + } + return; + } + + // add group package + String[] subs = className.split("\\."); + if (subs.length > 3) { + modified = allowedPrefix.add(subs[0] + "." + subs[1] + "." + subs[2]); + } else { + modified = allowedPrefix.add(className); + } + + if (modified) { + notifyListeners(); + } + } + + public void registerListener(AllowClassNotifyListener listener) { + listeners.add(listener); + listener.notify(checkStatus, allowedPrefix); + } + + private void notifyListeners() { + for (AllowClassNotifyListener listener : listeners) { + listener.notify(checkStatus, allowedPrefix); + } + } + + protected Set getAllowedPrefix() { + return allowedPrefix; + } +} diff --git a/dubbo-common/src/main/resources/security/serialize.allowlist b/dubbo-common/src/main/resources/security/serialize.allowlist new file mode 100644 index 0000000000..bca85c1ca0 --- /dev/null +++ b/dubbo-common/src/main/resources/security/serialize.allowlist @@ -0,0 +1,125 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +boolean +byte +char +double +float +int +long +short +java.lang.AutoCloseable +java.lang.Boolean +java.lang.Byte +java.lang.Character +java.lang.Class +java.lang.Cloneable +java.lang.Double +java.lang.Exception +java.lang.Float +java.lang.IllegalAccessError +java.lang.IllegalAccessException +java.lang.IllegalArgumentException +java.lang.IllegalMonitorStateException +java.lang.IllegalStateException +java.lang.IllegalThreadStateException +java.lang.IndexOutOfBoundsException +java.lang.InstantiationError +java.lang.InstantiationException +java.lang.Integer +java.lang.InternalError +java.lang.InterruptedException +java.lang.LinkageError +java.lang.Long +java.lang.NegativeArraySizeException +java.lang.NoClassDefFoundError +java.lang.NoSuchFieldError +java.lang.NoSuchFieldException +java.lang.NoSuchMethodError +java.lang.NoSuchMethodException +java.lang.NullPointerException +java.lang.Number +java.lang.NumberFormatException +java.lang.Object +java.lang.OutOfMemoryError +java.lang.RuntimeException +java.lang.SecurityException +java.lang.Short +java.lang.StackOverflowError +java.lang.StackTraceElement +java.lang.String +java.lang.StringIndexOutOfBoundsException +java.lang.TypeNotPresentException +java.lang.VerifyError +java.math.BigDecimal +java.math.BigInteger +java.text.SimpleDateFormat +java.time.format.DateTimeFormatter +java.time.Instant +java.time.LocalDate +java.time.LocalDateTime +java.time.LocalTime +java.util.ArrayList +java.util.Arrays$ArrayList +java.util.BitSet +java.util.Calendar +java.util.Collections$EmptyList +java.util.Collections$EmptyMap +java.util.Collections$SingletonSet +java.util.Collections$UnmodifiableCollection +java.util.Collections$UnmodifiableList +java.util.Collections$UnmodifiableMap +java.util.Collections$UnmodifiableNavigableMap +java.util.Collections$UnmodifiableNavigableSet +java.util.Collections$UnmodifiableRandomAccessList +java.util.Collections$UnmodifiableSet +java.util.Collections$UnmodifiableSortedMap +java.util.Collections$UnmodifiableSortedSet +java.util.concurrent.atomic.AtomicBoolean +java.util.concurrent.atomic.AtomicInteger +java.util.concurrent.atomic.AtomicIntegerArray +java.util.concurrent.atomic.AtomicLong +java.util.concurrent.atomic.AtomicLongArray +java.util.concurrent.atomic.AtomicReference +java.util.concurrent.ConcurrentHashMap +java.util.concurrent.ConcurrentLinkedQueue +java.util.concurrent.ConcurrentMap +java.util.concurrent.ConcurrentSkipListMap +java.util.concurrent.ConcurrentSkipListSet +java.util.concurrent.CopyOnWriteArrayList +java.util.concurrent.TimeUnit +java.util.Currency +java.util.Date +java.util.EnumSet +java.util.HashMap +java.util.HashSet +java.util.Hashtable +java.util.IdentityHashMap +java.util.LinkedHashMap +java.util.LinkedHashSet +java.util.LinkedList +java.util.List +java.util.Locale +java.util.Map +java.util.Set +java.util.TreeMap +java.util.TreeSet +java.util.UUID +java.util.WeakHashMap +org.apache.dubbo diff --git a/dubbo-common/src/main/resources/security/serialize.blockedlist b/dubbo-common/src/main/resources/security/serialize.blockedlist index de0b68de63..07bc753ccf 100644 --- a/dubbo-common/src/main/resources/security/serialize.blockedlist +++ b/dubbo-common/src/main/resources/security/serialize.blockedlist @@ -18,33 +18,38 @@ # aj.org.objectweb.asm. br.com.anteros. +bsh. ch.qos.logback. -clojure.core$constantly -clojure.main$eval_opt -com.alibaba.citrus.springext.support.parser.abstractnamedproxybeandefinitionparser$proxytargetfactory -com.alibaba.citrus.springext.util.springextutil.abstractproxy -com.alibaba.druid.pool.druiddatasource +clojure. +com.alibaba.citrus.springext.support.parser. +com.alibaba.citrus.springext.util.SpringExtUtil. +com.alibaba.druid.pool. com.alibaba.druid.stat.jdbcdatasourcestat com.alibaba.fastjson.annotation -com.alipay.custrelation.service.model.redress.pair +com.alibaba.hotcode.internal.org.apache.commons.collections.functors. +com.alipay.custrelation.service.model.redress. +com.alipay.oceanbase.obproxy.druid.pool. com.caucho. com.ibatis. +com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller. +com.ibm.xltxe.rnm1.xtq.bcel.util. com.mchange com.mysql.cj.jdbc.admin. com.mysql.cj.jdbc.mysqlconnectionpooldatasource com.mysql.cj.jdbc.mysqldatasource com.mysql.cj.jdbc.mysqlxadatasource com.mysql.cj.log. +com.mysql.jdbc.util. com.p6spy.engine. -com.rometools.rome.feed.impl.equalsbean -com.rometools.rome.feed.impl.tostringbean +com.rometools.rome.feed. com.sun. com.taobao.eagleeye.wrapper +com.taobao.vipserver.commons.collections.functors. com.zaxxer.hikari. flex.messaging.util.concurrent. -java.awt.i -java.awt.p -java.beans.expression +groovy.lang. +java.awt. +java.beans. java.io.closeable java.io.serializable java.lang.autocloseable @@ -52,41 +57,46 @@ java.lang.class java.lang.cloneable java.lang.iterable java.lang.object +java.lang.ProcessBuilder java.lang.readable java.lang.runnable +java.lang.Runtime java.lang.thread java.lang.unixprocess java.net.inetaddress java.net.socket java.net.url java.rmi -java.security.signedobject +java.security. java.util.collection java.util.eventlistener java.util.jar. java.util.logging. java.util.prefs. +java.util.ServiceLoader java.util.serviceloader$lazyiterator +java.util.StringTokenizer javassist. javax.activation. -javax.imageio.imageio$containsfilter -javax.imageio.spi.serviceregistry +javax.imageio. javax.management. +javax.media.jai.remote. javax.naming. javax.net. javax.print. javax.script. javax.sound. -javax.swing.j +javax.swing. javax.tools. javax.xml jdk.internal. jodd.db.connection. junit. -net.bytebuddy.dynamic.loading.bytearrayclassloader +net.bytebuddy.dynamic.loading. net.sf.cglib. net.sf.ehcache.hibernate. net.sf.ehcache.transaction.manager. +ognl. oracle.jdbc. oracle.jms.aq oracle.net @@ -100,10 +110,12 @@ org.apache.aries.transaction. org.apache.axis2.jaxws.spi.handler. org.apache.axis2.transport.jms. org.apache.bcel +org.apache.carbondata.core.scan.expression. org.apache.carbondata.core.scan.expression.expressionresult org.apache.catalina. org.apache.cocoon. org.apache.commons.beanutils +org.apache.commons.codec. org.apache.commons.collections.comparators. org.apache.commons.collections.functors org.apache.commons.collections.functors. @@ -112,6 +124,7 @@ org.apache.commons.collections4.comparators org.apache.commons.collections4.functors org.apache.commons.collections4.transformer org.apache.commons.configuration +org.apache.commons.configuration2. org.apache.commons.dbcp org.apache.commons.fileupload org.apache.commons.jelly. @@ -130,32 +143,43 @@ org.apache.ibatis.ognl. org.apache.ibatis.parsing. org.apache.ibatis.reflection. org.apache.ibatis.scripting. +org.apache.ignite.cache. org.apache.ignite.cache.jta. +org.apache.log.output.db. org.apache.log4j. org.apache.logging. org.apache.myfaces.context.servlet +org.apache.myfaces.view.facelets.el. org.apache.openjpa.ee. -org.apache.shiro.jndi. -org.apache.shiro.realm. +org.apache.shiro. org.apache.tomcat +org.apache.velocity. org.apache.wicket.util org.apache.xalan org.apache.xbean. -org.apache.xpath.xpathcontext +org.apache.xpath. +org.apache.zookeeper. +org.aspectj. org.codehaus.groovy.runtime org.codehaus.jackson. +org.datanucleus.store.rdbms.datasource.dbcp.datasources. +org.dom4j. org.eclipse.jetty. -org.geotools.filter.constantexpression +org.geotools.filter. org.h2.jdbcx. org.h2.server. +org.h2.value. org.hibernate org.javasimon. org.jaxen. org.jboss org.jdom. org.jdom2.transform. +org.junit. org.logicalcobwebs. +org.mockito. org.mortbay.jetty. +org.mortbay.log. org.mozilla.javascript org.objectweb.asm. org.osjava.sj. @@ -163,5 +187,11 @@ org.python.core org.quartz. org.slf4j. org.springframework. -org.yaml.snakeyaml.tokens.directivetoken -sun.rmi.server.unicastref \ No newline at end of file +org.thymeleaf. +org.yaml.snakeyaml.tokens. +pstore.shaded.org.apache.commons.collections. +sun.print. +sun.rmi.server. +sun.rmi.transport. +weblogic.ejb20.internal. +weblogic.jms.common. diff --git a/dubbo-common/src/test/java/com/pojo/Demo1.java b/dubbo-common/src/test/java/com/pojo/Demo1.java new file mode 100644 index 0000000000..6713821437 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo1.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo1 { + private Simple simple; + + public Simple getSimple() { + return simple; + } + + public void setSimple(Simple simple) { + this.simple = simple; + } +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo2.java b/dubbo-common/src/test/java/com/pojo/Demo2.java new file mode 100644 index 0000000000..9f7f5620b9 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo2.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo2 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo3.java b/dubbo-common/src/test/java/com/pojo/Demo3.java new file mode 100644 index 0000000000..ea1f65baff --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo3.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo3 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo4.java b/dubbo-common/src/test/java/com/pojo/Demo4.java new file mode 100644 index 0000000000..3f12cca518 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo4.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo4 extends Demo3 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo5.java b/dubbo-common/src/test/java/com/pojo/Demo5.java new file mode 100644 index 0000000000..e306ed1af5 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo5.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo5 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo6.java b/dubbo-common/src/test/java/com/pojo/Demo6.java new file mode 100644 index 0000000000..890f6e2df8 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo6.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo6 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo7.java b/dubbo-common/src/test/java/com/pojo/Demo7.java new file mode 100644 index 0000000000..06771c28a4 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo7.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo7 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Demo8.java b/dubbo-common/src/test/java/com/pojo/Demo8.java new file mode 100644 index 0000000000..b991d5f3e2 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Demo8.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Demo8 { +} diff --git a/dubbo-common/src/test/java/com/pojo/DemoException1.java b/dubbo-common/src/test/java/com/pojo/DemoException1.java new file mode 100644 index 0000000000..0555fc7207 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/DemoException1.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class DemoException1 extends Exception{ +} diff --git a/dubbo-common/src/test/java/com/pojo/DemoException2.java b/dubbo-common/src/test/java/com/pojo/DemoException2.java new file mode 100644 index 0000000000..e880cfef7e --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/DemoException2.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class DemoException2 extends Exception { +} diff --git a/dubbo-common/src/test/java/com/pojo/DemoException3.java b/dubbo-common/src/test/java/com/pojo/DemoException3.java new file mode 100644 index 0000000000..f842b0b4c7 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/DemoException3.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class DemoException3 extends DemoException2 { +} diff --git a/dubbo-common/src/test/java/com/pojo/Simple.java b/dubbo-common/src/test/java/com/pojo/Simple.java new file mode 100644 index 0000000000..62a1161292 --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/Simple.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pojo; + +public class Simple { +} diff --git a/dubbo-common/src/test/java/com/service/DemoService1.java b/dubbo-common/src/test/java/com/service/DemoService1.java new file mode 100644 index 0000000000..139fb69624 --- /dev/null +++ b/dubbo-common/src/test/java/com/service/DemoService1.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.service; + +import com.pojo.Demo1; +import com.pojo.Demo2; +import com.pojo.Demo4; +import com.pojo.Demo5; +import com.pojo.Demo6; +import com.pojo.Demo7; +import com.pojo.Demo8; +import com.pojo.DemoException1; +import com.pojo.DemoException3; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +public interface DemoService1 { + Demo1 getDemo1(); + + void setDemo2(Demo2 demo2); + + List getDemo4s(); + + List>>>>> getDemo5s(); + + List[] getDemo7s(); + + List getTs(); + + void echo1() throws DemoException1; + + void echo2() throws DemoException3; +} diff --git a/dubbo-common/src/test/java/com/service/DemoService2.java b/dubbo-common/src/test/java/com/service/DemoService2.java new file mode 100644 index 0000000000..ac2910bf7c --- /dev/null +++ b/dubbo-common/src/test/java/com/service/DemoService2.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.service; + +public interface DemoService2 extends DemoService1 { +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityManagerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityManagerTest.java new file mode 100644 index 0000000000..69eeaf2c25 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityManagerTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.utils; + +import org.apache.dubbo.rpc.model.FrameworkModel; + +import com.service.DemoService1; +import com.service.DemoService2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +public class SerializeSecurityManagerTest { + @Test + public void test() { + SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel()); + ssm.registerListener(new TestAllowClassNotifyListener()); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.HashMap")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.DemoInterface")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.sun.Interface1")); + Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2")); + + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + } + + @Test + public void addToAllow() { + SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel()); + ssm.registerListener(new TestAllowClassNotifyListener()); + Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + + ssm.addToAllow("com.sun.Interface2"); + Assertions.assertFalse(ssm.getAllowedPrefix().contains("com.sun.Interface2")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + + ssm.addToAllow("java.util.Interface1"); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.Interface1")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + + ssm.addToAllow("java.util.package.Interface1"); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("java.util.package.Interface1")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + + ssm.addToAllow("com.example.Interface2"); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.Interface2")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + + ssm.addToAllow("com.example.package.Interface1"); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.example.package")); + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + } + + @Test + public void testRegister1() { + SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel()); + ssm.registerListener(new TestAllowClassNotifyListener()); + + ssm.registerInterface(DemoService1.class); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService1")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple")); + + Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName())); + + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + } + + + @Test + public void testRegister2() { + SerializeSecurityManager ssm = new SerializeSecurityManager(FrameworkModel.defaultModel()); + ssm.registerListener(new TestAllowClassNotifyListener()); + + ssm.registerInterface(DemoService2.class); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.service.DemoService2")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo1")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo2")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo3")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo4")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo5")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo6")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo7")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Demo8")); + Assertions.assertTrue(ssm.getAllowedPrefix().contains("com.pojo.Simple")); + + Assertions.assertTrue(ssm.getAllowedPrefix().contains(List.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Set.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Map.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(LinkedList.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(Vector.class.getName())); + Assertions.assertTrue(ssm.getAllowedPrefix().contains(HashSet.class.getName())); + + Assertions.assertEquals(ssm.getAllowedPrefix(), TestAllowClassNotifyListener.getPrefixList()); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/DefaultHessian2FactoryInitializer.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/TestAllowClassNotifyListener.java similarity index 53% rename from dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/DefaultHessian2FactoryInitializer.java rename to dubbo-common/src/test/java/org/apache/dubbo/common/utils/TestAllowClassNotifyListener.java index cca8697030..362939cc4d 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/DefaultHessian2FactoryInitializer.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/TestAllowClassNotifyListener.java @@ -14,18 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.common.serialize.hessian2.dubbo; +package org.apache.dubbo.common.utils; -import org.apache.dubbo.common.serialize.hessian2.Hessian2SerializerFactory; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; -import com.alibaba.com.caucho.hessian.io.SerializerFactory; +public class TestAllowClassNotifyListener implements AllowClassNotifyListener { + private final static AtomicReference> prefixList = new AtomicReference<>(); -public class DefaultHessian2FactoryInitializer extends AbstractHessian2FactoryInitializer { @Override - protected SerializerFactory createSerializerFactory() { - Hessian2SerializerFactory hessian2SerializerFactory = new Hessian2SerializerFactory(); - hessian2SerializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian.allowNonSerializable", "false"))); - hessian2SerializerFactory.getClassFactory().allow("org.apache.dubbo.*"); - return hessian2SerializerFactory; + public void notify(SerializeCheckStatus status, Set prefixList) { + TestAllowClassNotifyListener.prefixList.set(prefixList); + } + + public static Set getPrefixList() { + return prefixList.get(); } } diff --git a/dubbo-common/src/test/resources/security/serialize.allowlist b/dubbo-common/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..0e7c6e7208 --- /dev/null +++ b/dubbo-common/src/test/resources/security/serialize.allowlist @@ -0,0 +1,20 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +com.example.DemoInterface +com.sun.Interface1 diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java index c019027c0e..043c974170 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java @@ -1038,6 +1038,7 @@ class ReferenceConfigTest { @Test @DisabledForJreRange(min = JRE.JAVA_16) public void testDifferentClassLoaderRequest() throws Exception { + FrameworkModel frameworkModel = FrameworkModel.defaultModel(); String basePath = DemoService.class.getProtectionDomain().getCodeSource().getLocation().getFile(); basePath = java.net.URLDecoder.decode(basePath, "UTF-8"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -1046,7 +1047,7 @@ class ReferenceConfigTest { TestClassLoader2 classLoader3 = new TestClassLoader2(classLoader2, basePath); ApplicationConfig applicationConfig = new ApplicationConfig("TestApp"); - ApplicationModel applicationModel = new ApplicationModel(FrameworkModel.defaultModel()); + ApplicationModel applicationModel = new ApplicationModel(frameworkModel); applicationModel.getApplicationConfigManager().setApplication(applicationConfig); ModuleModel moduleModel = new ModuleModel(applicationModel); diff --git a/dubbo-config/dubbo-config-api/src/test/resources/security/serialize.allowlist b/dubbo-config/dubbo-config-api/src/test/resources/security/serialize.allowlist new file mode 100644 index 0000000000..ddd7fa8e4c --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/test/resources/security/serialize.allowlist @@ -0,0 +1,19 @@ +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +demo. diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index d726d7a7b7..53d6e84c1d 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -887,4 +887,5 @@ + diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml index eb8efb463d..ef0846f94d 100644 --- a/dubbo-distribution/dubbo-all/pom.xml +++ b/dubbo-distribution/dubbo-all/pom.xml @@ -657,12 +657,6 @@ META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization - - - META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer - - diff --git a/dubbo-distribution/dubbo-core-spi/pom.xml b/dubbo-distribution/dubbo-core-spi/pom.xml index f4f77891bd..54ad09ddd5 100644 --- a/dubbo-distribution/dubbo-core-spi/pom.xml +++ b/dubbo-distribution/dubbo-core-spi/pom.xml @@ -274,12 +274,6 @@ META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization - - - META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer - - diff --git a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json index 452730e2b0..be002ae8a5 100644 --- a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json +++ b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json @@ -1065,19 +1065,6 @@ } ] }, - { - "name": "org.apache.dubbo.common.serialize.hessian2.dubbo.DefaultHessian2FactoryInitializer", - "allPublicMethods": true, - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "org.apache.dubbo.common.serialize.hessian2.dubbo.WhitelistHessian2FactoryInitializer" - }, { "name": "org.apache.dubbo.common.status.reporter.FrameworkStatusReportService", "methods": [ diff --git a/dubbo-native-plugin/src/main/resources/META-INF/native-image/resource-config.json b/dubbo-native-plugin/src/main/resources/META-INF/native-image/resource-config.json index 977e6eb3d1..f8deec494a 100644 --- a/dubbo-native-plugin/src/main/resources/META-INF/native-image/resource-config.json +++ b/dubbo-native-plugin/src/main/resources/META-INF/native-image/resource-config.json @@ -28,9 +28,6 @@ { "pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization\\E" }, - { - "pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer\\E" - }, { "pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool\\E" }, diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSecurityWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSecurityWrapper.java new file mode 100644 index 0000000000..d343009377 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolSecurityWrapper.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.SerializeSecurityManager; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Protocol; +import org.apache.dubbo.rpc.ProtocolServer; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.model.ScopeModel; +import org.apache.dubbo.rpc.model.ScopeModelUtil; +import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.model.ServiceModel; + +import java.util.List; +import java.util.Optional; + +@Activate(order = 200) +public class ProtocolSecurityWrapper implements Protocol { + private final Protocol protocol; + + public ProtocolSecurityWrapper(Protocol protocol) { + if (protocol == null) { + throw new IllegalArgumentException("protocol == null"); + } + this.protocol = protocol; + } + + @Override + public int getDefaultPort() { + return protocol.getDefaultPort(); + } + + @Override + public Exporter export(Invoker invoker) throws RpcException { + ServiceModel serviceModel = invoker.getUrl().getServiceModel(); + ScopeModel scopeModel = invoker.getUrl().getScopeModel(); + Optional.ofNullable(serviceModel) + .map(ServiceModel::getServiceModel) + .map(ServiceDescriptor::getServiceInterfaceClass) + .ifPresent((interfaceClass) -> { + SerializeSecurityManager serializeSecurityManager = ScopeModelUtil.getFrameworkModel(scopeModel) + .getBeanFactory().getBean(SerializeSecurityManager.class); + serializeSecurityManager.registerInterface(interfaceClass); + }); + return protocol.export(invoker); + } + + @Override + public Invoker refer(Class type, URL url) throws RpcException { + ServiceModel serviceModel = url.getServiceModel(); + ScopeModel scopeModel = url.getScopeModel(); + SerializeSecurityManager serializeSecurityManager = ScopeModelUtil.getFrameworkModel(scopeModel) + .getBeanFactory().getBean(SerializeSecurityManager.class); + + Optional.ofNullable(serviceModel) + .map(ServiceModel::getServiceModel) + .map(ServiceDescriptor::getServiceInterfaceClass) + .ifPresent(serializeSecurityManager::registerInterface); + serializeSecurityManager.registerInterface(type); + + return protocol.refer(type, url); + } + + @Override + public void destroy() { + protocol.destroy(); + } + + @Override + public List getServers() { + return protocol.getServers(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol index ab69b9c74f..eff2b090d6 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol +++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol @@ -1,3 +1,4 @@ listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper mock=org.apache.dubbo.rpc.support.MockProtocol serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper +securitywrapper=org.apache.dubbo.rpc.protocol.ProtocolSecurityWrapper diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java index 10b14dd6cc..d5975fdba6 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java @@ -32,11 +32,15 @@ public class FastJson2ObjectInput implements ObjectInput { private final Fastjson2CreatorManager fastjson2CreatorManager; + private final Fastjson2SecurityManager fastjson2SecurityManager; + private volatile ClassLoader classLoader; private final InputStream is; - public FastJson2ObjectInput(Fastjson2CreatorManager fastjson2CreatorManager, InputStream in) { + public FastJson2ObjectInput(Fastjson2CreatorManager fastjson2CreatorManager, + Fastjson2SecurityManager fastjson2SecurityManager, InputStream in) { this.fastjson2CreatorManager = fastjson2CreatorManager; + this.fastjson2SecurityManager = fastjson2SecurityManager; this.classLoader = Thread.currentThread().getContextClassLoader(); this.is = in; fastjson2CreatorManager.setCreator(classLoader); @@ -107,8 +111,9 @@ public class FastJson2ObjectInput implements ObjectInput { if (read != length) { throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read); } - return (T) JSONB.parseObject(bytes, Object.class, JSONReader.Feature.SupportAutoType, + return (T) JSONB.parseObject(bytes, Object.class, fastjson2SecurityManager.getSecurityFilter(), JSONReader.Feature.UseDefaultConstructorAsPossible, + JSONReader.Feature.ErrorOnNoneSerializable, JSONReader.Feature.UseNativeObject, JSONReader.Feature.FieldBased); } @@ -123,8 +128,9 @@ public class FastJson2ObjectInput implements ObjectInput { if (read != length) { throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read); } - return (T) JSONB.parseObject(bytes, Object.class, JSONReader.Feature.SupportAutoType, + return (T) JSONB.parseObject(bytes, Object.class, fastjson2SecurityManager.getSecurityFilter(), JSONReader.Feature.UseDefaultConstructorAsPossible, + JSONReader.Feature.ErrorOnNoneSerializable, JSONReader.Feature.UseNativeObject, JSONReader.Feature.FieldBased); } diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java index fdf15aeba3..97c099f27b 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java @@ -98,6 +98,7 @@ public class FastJson2ObjectOutput implements ObjectOutput { updateClassLoaderIfNeed(); byte[] bytes = JSONB.toBytes(obj, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.FieldBased, + JSONWriter.Feature.ErrorOnNoneSerializable, JSONWriter.Feature.ReferenceDetection, JSONWriter.Feature.WriteNulls, JSONWriter.Feature.NotWriteDefaultValue, diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java index 4c20abcc8a..1e6b636787 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java @@ -39,8 +39,11 @@ public class FastJson2Serialization implements Serialization { private final Fastjson2CreatorManager fastjson2CreatorManager; + private final Fastjson2SecurityManager fastjson2SecurityManager; + public FastJson2Serialization(FrameworkModel frameworkModel) { this.fastjson2CreatorManager = frameworkModel.getBeanFactory().getBean(Fastjson2CreatorManager.class); + this.fastjson2SecurityManager = frameworkModel.getBeanFactory().getBean(Fastjson2SecurityManager.class); } @Override @@ -60,7 +63,7 @@ public class FastJson2Serialization implements Serialization { @Override public ObjectInput deserialize(URL url, InputStream input) throws IOException { - return new FastJson2ObjectInput(fastjson2CreatorManager, input); + return new FastJson2ObjectInput(fastjson2CreatorManager, fastjson2SecurityManager, input); } } diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java index d9c9d25885..db256cce38 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java @@ -23,10 +23,12 @@ import org.apache.dubbo.rpc.model.ModuleModel; import org.apache.dubbo.rpc.model.ScopeModelInitializer; public class Fastjson2ScopeModelInitializer implements ScopeModelInitializer { + @Override public void initializeFrameworkModel(FrameworkModel frameworkModel) { ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); beanFactory.registerBean(Fastjson2CreatorManager.class); + beanFactory.registerBean(Fastjson2SecurityManager.class); } @Override diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java new file mode 100644 index 0000000000..7259a0630d --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.fastjson2; + +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.AllowClassNotifyListener; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.common.utils.SerializeCheckStatus; +import org.apache.dubbo.common.utils.SerializeSecurityManager; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import com.alibaba.fastjson2.filter.ContextAutoTypeBeforeHandler; +import com.alibaba.fastjson2.filter.Filter; +import com.alibaba.fastjson2.util.TypeUtils; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static com.alibaba.fastjson2.util.TypeUtils.loadClass; + +public class Fastjson2SecurityManager implements AllowClassNotifyListener { + private Filter securityFilter = new Handler(AllowClassNotifyListener.DEFAULT_STATUS, new String[0]); + + private final static Logger logger = LoggerFactory.getLogger(Fastjson2SecurityManager.class); + + private final static Set warnedClasses = new ConcurrentHashSet<>(1); + + public Fastjson2SecurityManager(FrameworkModel frameworkModel) { + SerializeSecurityManager securityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); + securityManager.registerListener(this); + } + + public void notify(SerializeCheckStatus status, Set prefixList) { + this.securityFilter = new Handler(status, prefixList.toArray(new String[0])); + } + + public Filter getSecurityFilter() { + return securityFilter; + } + + public static class Handler extends ContextAutoTypeBeforeHandler { + final SerializeCheckStatus status; + final Map> classCache = new ConcurrentHashMap<>(16, 0.75f, 1); + + public Handler(SerializeCheckStatus status, String[] acceptNames) { + super(true, acceptNames); + this.status = status; + } + + @Override + public Class apply(String typeName, Class expectClass, long features) { + switch (status) { + case STRICT: + return super.apply(typeName, expectClass, features); + case WARN: + Class tryLoad = super.apply(typeName, expectClass, features); + if (tryLoad != null) { + return tryLoad; + } + case DISABLED: + Class localClass = loadClassDirectly(typeName); + if (localClass != null) { + if (status == SerializeCheckStatus.WARN && warnedClasses.add(typeName)) { + logger.error("[Serialization Security] Serialized class " + localClass.getName() + " is not in allow list. " + + "Current mode is `WARN`, will allow to deserialize it by default. " + + "Dubbo will set to `STRICT` mode by default in the future. " + + "Please add it into security/serialize.allowlist or follow FAQ to configure it."); + } + return localClass; + } + default: + return null; + } + } + + public Class loadClassDirectly(String typeName) { + Class clazz = classCache.get(typeName); + + if (clazz == null) { + clazz = TypeUtils.getMapping(typeName); + } + + if (clazz == null) { + clazz = loadClass(typeName); + } + + if (clazz != null) { + Class origin = classCache.putIfAbsent(typeName, clazz); + if (origin != null) { + clazz = origin; + } + } + + + return clazz; + } + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java new file mode 100644 index 0000000000..906b5878df --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.AllowClassNotifyListener; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.common.utils.SerializeCheckStatus; +import org.apache.dubbo.common.utils.SerializeSecurityManager; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import java.util.Arrays; +import java.util.Set; + +/** + * Inspired by Fastjson2 + * see com.alibaba.fastjson2.filter.ContextAutoTypeBeforeHandler#apply(java.lang.String, java.lang.Class, long) + */ +public class Hessian2AllowClassManager implements AllowClassNotifyListener { + private static final long MAGIC_HASH_CODE = 0xcbf29ce484222325L; + private static final long MAGIC_PRIME = 0x100000001b3L; + private static final Logger logger = LoggerFactory.getLogger(Hessian2AllowClassManager.class); + private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS; + private final static Set warnedClasses = new ConcurrentHashSet<>(1); + private volatile long[] allowPrefixes = new long[0]; + + public Hessian2AllowClassManager(FrameworkModel frameworkModel) { + SerializeSecurityManager serializeSecurityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); + serializeSecurityManager.registerListener(this); + } + + @Override + public void notify(SerializeCheckStatus status, Set prefixList) { + this.checkStatus = status; + long[] array = new long[prefixList.size()]; + + int index = 0; + for (String name : prefixList) { + if (name == null || name.isEmpty()) { + continue; + } + + long hashCode = MAGIC_HASH_CODE; + for (int j = 0; j < name.length(); ++j) { + char ch = name.charAt(j); + if (ch == '$') { + ch = '.'; + } + hashCode ^= ch; + hashCode *= MAGIC_PRIME; + } + + array[index++] = hashCode; + } + + if (index != array.length) { + array = Arrays.copyOf(array, index); + } + Arrays.sort(array); + this.allowPrefixes = array; + } + + public Class loadClass(ClassLoader classLoader, String className) throws ClassNotFoundException { + if (checkStatus == SerializeCheckStatus.DISABLED) { + return Class.forName(className, false, classLoader); + } + + long hash = MAGIC_HASH_CODE; + for (int i = 0, typeNameLength = className.length(); i < typeNameLength; ++i) { + char ch = className.charAt(i); + if (ch == '$') { + ch = '.'; + } + hash ^= ch; + hash *= MAGIC_PRIME; + + if (Arrays.binarySearch(allowPrefixes, hash) >= 0) { + return Class.forName(className, false, classLoader); + } + } + + if (checkStatus == SerializeCheckStatus.STRICT) { + String msg = "[Serialization Security] Serialized class " + className + " is not in allow list. " + + "Current mode is `STRICT`, will disallow to deserialize it by default. " + + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; + if (warnedClasses.add(className)) { + logger.error(msg); + } + + throw new IllegalArgumentException(msg); + } else { + Class clazz = Class.forName(className, false, classLoader); + if (warnedClasses.add(className)) { + logger.error("[Serialization Security] Serialized class " + clazz.getName() + " is not in allow list. " + + "Current mode is `WARN`, will allow to deserialize it by default. " + + "Dubbo will set to `STRICT` mode by default in the future. " + + "Please add it into security/serialize.allowlist or follow FAQ to configure it."); + } + return clazz; + } + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ClassLoaderListener.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ClassLoaderListener.java new file mode 100644 index 0000000000..13b0b2462d --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ClassLoaderListener.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.model.ScopeClassLoaderListener; + +public class Hessian2ClassLoaderListener implements ScopeClassLoaderListener { + @Override + public void onAddClassLoader(FrameworkModel scopeModel, ClassLoader classLoader) { + // noop + } + + @Override + public void onRemoveClassLoader(FrameworkModel scopeModel, ClassLoader classLoader) { + Hessian2FactoryManager hessian2FactoryManager = scopeModel.getBeanFactory().getBean(Hessian2FactoryManager.class); + hessian2FactoryManager.onRemoveClassLoader(classLoader); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java new file mode 100644 index 0000000000..a572925fef --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2FactoryManager.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.model.FrameworkModel; + +import com.alibaba.com.caucho.hessian.io.SerializerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class Hessian2FactoryManager { + String WHITELIST = "dubbo.application.hessian2.whitelist"; + String ALLOW = "dubbo.application.hessian2.allow"; + String DENY = "dubbo.application.hessian2.deny"; + private volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY; + private final Map CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>(); + + private final Hessian2AllowClassManager hessian2AllowClassManager; + + public Hessian2FactoryManager(FrameworkModel frameworkModel) { + hessian2AllowClassManager = new Hessian2AllowClassManager(frameworkModel); + } + + public SerializerFactory getSerializerFactory(ClassLoader classLoader) { + if (classLoader == null) { + // system classloader + if (SYSTEM_SERIALIZER_FACTORY == null) { + synchronized (this) { + if (SYSTEM_SERIALIZER_FACTORY == null) { + SYSTEM_SERIALIZER_FACTORY = createSerializerFactory(); + } + } + } + return SYSTEM_SERIALIZER_FACTORY; + } + + if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) { + synchronized (this) { + if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) { + SerializerFactory serializerFactory = createSerializerFactory(); + CL_2_SERIALIZER_FACTORY.put(classLoader, serializerFactory); + return serializerFactory; + } + } + } + return CL_2_SERIALIZER_FACTORY.get(classLoader); + } + + private SerializerFactory createSerializerFactory() { + String whitelist = System.getProperty(WHITELIST); + if (StringUtils.isNotEmpty(whitelist)) { + return createWhiteListSerializerFactory(); + } + + return createDefaultSerializerFactory(); + } + + private SerializerFactory createDefaultSerializerFactory() { + Hessian2SerializerFactory hessian2SerializerFactory = new Hessian2SerializerFactory(hessian2AllowClassManager); + hessian2SerializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian.allowNonSerializable", "false"))); + hessian2SerializerFactory.getClassFactory().allow("org.apache.dubbo.*"); + return hessian2SerializerFactory; + } + + public SerializerFactory createWhiteListSerializerFactory() { + SerializerFactory serializerFactory = new Hessian2SerializerFactory(hessian2AllowClassManager); + String whiteList = System.getProperty(WHITELIST); + if ("true".equals(whiteList)) { + serializerFactory.getClassFactory().setWhitelist(true); + String allowPattern = System.getProperty(ALLOW); + if (StringUtils.isNotEmpty(allowPattern)) { + for (String pattern : allowPattern.split(";")) { + serializerFactory.getClassFactory().allow(pattern); + } + } + } else { + serializerFactory.getClassFactory().setWhitelist(false); + String denyPattern = System.getProperty(DENY); + if (StringUtils.isNotEmpty(denyPattern)) { + for (String pattern : denyPattern.split(";")) { + serializerFactory.getClassFactory().deny(pattern); + } + } + } + serializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian.allowNonSerializable", "false"))); + serializerFactory.getClassFactory().allow("org.apache.dubbo.*"); + return serializerFactory; + } + + public void onRemoveClassLoader(ClassLoader classLoader) { + CL_2_SERIALIZER_FACTORY.remove(classLoader); + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java index 47aea6f2bc..f345621cc0 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java @@ -18,7 +18,7 @@ package org.apache.dubbo.common.serialize.hessian2; import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectInput; -import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer; +import org.apache.dubbo.rpc.model.FrameworkModel; import com.alibaba.com.caucho.hessian.io.Hessian2Input; @@ -31,12 +31,19 @@ import java.lang.reflect.Type; */ public class Hessian2ObjectInput implements ObjectInput, Cleanable { private final Hessian2Input mH2i; - private final Hessian2FactoryInitializer hessian2FactoryInitializer; + private final Hessian2FactoryManager hessian2FactoryManager; + @Deprecated public Hessian2ObjectInput(InputStream is) { mH2i = new Hessian2Input(is); - hessian2FactoryInitializer = Hessian2FactoryInitializer.getInstance(); - mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory()); + this.hessian2FactoryManager = FrameworkModel.defaultModel().getBeanFactory().getOrRegisterBean(Hessian2FactoryManager.class); + mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); + } + + public Hessian2ObjectInput(InputStream is, Hessian2FactoryManager hessian2FactoryManager) { + mH2i = new Hessian2Input(is); + this.hessian2FactoryManager = hessian2FactoryManager; + mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); } @Override @@ -87,7 +94,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable { @Override public Object readObject() throws IOException { if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) { - mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory()); + mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); } return mH2i.readObject(); } @@ -97,7 +104,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable { public T readObject(Class cls) throws IOException, ClassNotFoundException { if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) { - mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory()); + mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); } return (T) mH2i.readObject(cls); } @@ -105,7 +112,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable { @Override public T readObject(Class cls, Type type) throws IOException, ClassNotFoundException { if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) { - mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory()); + mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); } return readObject(cls); } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java index 858ab57cb0..27c1ebd91d 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java @@ -18,7 +18,7 @@ package org.apache.dubbo.common.serialize.hessian2; import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectOutput; -import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer; +import org.apache.dubbo.rpc.model.FrameworkModel; import com.alibaba.com.caucho.hessian.io.Hessian2Output; @@ -32,9 +32,16 @@ public class Hessian2ObjectOutput implements ObjectOutput, Cleanable { private final Hessian2Output mH2o; + @Deprecated public Hessian2ObjectOutput(OutputStream os) { mH2o = new Hessian2Output(os); - mH2o.setSerializerFactory(Hessian2FactoryInitializer.getInstance().getSerializerFactory()); + Hessian2FactoryManager hessian2FactoryManager = FrameworkModel.defaultModel().getBeanFactory().getOrRegisterBean(Hessian2FactoryManager.class); + mH2o.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); + } + + public Hessian2ObjectOutput(OutputStream os, Hessian2FactoryManager hessian2FactoryManager) { + mH2o = new Hessian2Output(os); + mH2o.setSerializerFactory(hessian2FactoryManager.getSerializerFactory(Thread.currentThread().getContextClassLoader())); } @Override diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java new file mode 100644 index 0000000000..578850905c --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ScopeModelInitializer.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.model.ModuleModel; +import org.apache.dubbo.rpc.model.ScopeModelInitializer; + +public class Hessian2ScopeModelInitializer implements ScopeModelInitializer { + @Override + public void initializeFrameworkModel(FrameworkModel frameworkModel) { + ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); + beanFactory.registerBean(Hessian2FactoryManager.class); + } + + @Override + public void initializeApplicationModel(ApplicationModel applicationModel) { + + } + + @Override + public void initializeModuleModel(ModuleModel moduleModel) { + + } +} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java index 010a5b6f31..aebff2fbf5 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.rpc.model.FrameworkModel; import java.io.IOException; import java.io.InputStream; @@ -36,6 +37,12 @@ import static org.apache.dubbo.common.serialize.Constants.HESSIAN2_SERIALIZATION */ public class Hessian2Serialization implements Serialization { + private final Hessian2FactoryManager hessian2FactoryManager; + + public Hessian2Serialization(FrameworkModel frameworkModel) { + hessian2FactoryManager = frameworkModel.getBeanFactory().getBean(Hessian2FactoryManager.class); + } + @Override public byte getContentTypeId() { return HESSIAN2_SERIALIZATION_ID; @@ -48,12 +55,12 @@ public class Hessian2Serialization implements Serialization { @Override public ObjectOutput serialize(URL url, OutputStream out) throws IOException { - return new Hessian2ObjectOutput(out); + return new Hessian2ObjectOutput(out, hessian2FactoryManager); } @Override public ObjectInput deserialize(URL url, InputStream is) throws IOException { - return new Hessian2ObjectInput(is); + return new Hessian2ObjectInput(is, hessian2FactoryManager); } } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java index 52cf50504d..e319cf7d61 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializerFactory.java @@ -20,7 +20,14 @@ import com.alibaba.com.caucho.hessian.io.SerializerFactory; public class Hessian2SerializerFactory extends SerializerFactory { - public Hessian2SerializerFactory() { + private Hessian2AllowClassManager hessian2AllowClassManager; + + public Hessian2SerializerFactory(Hessian2AllowClassManager hessian2AllowClassManager) { + this.hessian2AllowClassManager = hessian2AllowClassManager; } + @Override + public Class loadSerializedClass(String className) throws ClassNotFoundException { + return hessian2AllowClassManager.loadClass(getClassLoader(), className); + } } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java deleted file mode 100644 index 6bd18d22a1..0000000000 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/AbstractHessian2FactoryInitializer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.common.serialize.hessian2.dubbo; - -import com.alibaba.com.caucho.hessian.io.SerializerFactory; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public abstract class AbstractHessian2FactoryInitializer implements Hessian2FactoryInitializer { - private static final Map CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>(); - private static volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY; - - @Override - public SerializerFactory getSerializerFactory() { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader == null) { - // system classloader - if (SYSTEM_SERIALIZER_FACTORY == null) { - synchronized (AbstractHessian2FactoryInitializer.class) { - if (SYSTEM_SERIALIZER_FACTORY == null) { - SYSTEM_SERIALIZER_FACTORY = createSerializerFactory(); - } - } - } - return SYSTEM_SERIALIZER_FACTORY; - } - - if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) { - synchronized (AbstractHessian2FactoryInitializer.class) { - if (!CL_2_SERIALIZER_FACTORY.containsKey(classLoader)) { - SerializerFactory serializerFactory = createSerializerFactory(); - CL_2_SERIALIZER_FACTORY.put(classLoader, serializerFactory); - return serializerFactory; - } - } - } - return CL_2_SERIALIZER_FACTORY.get(classLoader); - } - - protected abstract SerializerFactory createSerializerFactory(); -} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/Hessian2FactoryInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/Hessian2FactoryInitializer.java deleted file mode 100644 index 842888cf1b..0000000000 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/Hessian2FactoryInitializer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.common.serialize.hessian2.dubbo; - -import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.common.extension.ExtensionScope; -import org.apache.dubbo.common.extension.SPI; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.rpc.model.FrameworkModel; - -import com.alibaba.com.caucho.hessian.io.SerializerFactory; - -@SPI(value = "default", scope = ExtensionScope.FRAMEWORK) -public interface Hessian2FactoryInitializer { - String ALLOW = System.getProperty("dubbo.application.hessian2.allow"); - String DENY = System.getProperty("dubbo.application.hessian2.deny"); - String WHITELIST = System.getProperty("dubbo.application.hessian2.whitelist"); - - String ALLOW_NON_SERIALIZABLE = System.getProperty("dubbo.hessian.allowNonSerializable", "false"); - - SerializerFactory getSerializerFactory(); - - static Hessian2FactoryInitializer getInstance() { - ExtensionLoader loader = FrameworkModel.defaultModel().getExtensionLoader(Hessian2FactoryInitializer.class); - if (StringUtils.isNotEmpty(WHITELIST)) { - return loader.getExtension("whitelist"); - } - return loader.getDefaultExtension(); - } - -} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/WhitelistHessian2FactoryInitializer.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/WhitelistHessian2FactoryInitializer.java deleted file mode 100644 index 53bcb0f0cb..0000000000 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/dubbo/WhitelistHessian2FactoryInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.common.serialize.hessian2.dubbo; - -import org.apache.dubbo.common.serialize.hessian2.Hessian2SerializerFactory; -import org.apache.dubbo.common.utils.StringUtils; - -import com.alibaba.com.caucho.hessian.io.SerializerFactory; - -/** - * see https://github.com/ebourg/hessian/commit/cf851f5131707891e723f7f6a9718c2461aed826 - */ -public class WhitelistHessian2FactoryInitializer extends AbstractHessian2FactoryInitializer { - - @Override - public SerializerFactory createSerializerFactory() { - SerializerFactory serializerFactory = new Hessian2SerializerFactory(); - if ("true".equals(WHITELIST)) { - serializerFactory.getClassFactory().setWhitelist(true); - if (StringUtils.isNotEmpty(ALLOW)) { - for (String pattern : ALLOW.split(";")) { - serializerFactory.getClassFactory().allow(pattern); - } - } - } else { - serializerFactory.getClassFactory().setWhitelist(false); - if (StringUtils.isNotEmpty(DENY)) { - for (String pattern : DENY.split(";")) { - serializerFactory.getClassFactory().deny(pattern); - } - } - } - serializerFactory.setAllowNonSerializable(Boolean.parseBoolean(ALLOW_NON_SERIALIZABLE)); - serializerFactory.getClassFactory().allow("org.apache.dubbo.*"); - return serializerFactory; - } - -} diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer b/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer deleted file mode 100644 index 460972e240..0000000000 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer +++ /dev/null @@ -1,2 +0,0 @@ -default=org.apache.dubbo.common.serialize.hessian2.dubbo.DefaultHessian2FactoryInitializer -whitelist=org.apache.dubbo.common.serialize.hessian2.dubbo.WhitelistHessian2FactoryInitializer \ No newline at end of file diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer b/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer new file mode 100644 index 0000000000..8f25eb7be8 --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer @@ -0,0 +1 @@ +hessian2=org.apache.dubbo.common.serialize.hessian2.Hessian2ScopeModelInitializer diff --git a/pom.xml b/pom.xml index 36f55fc0c4..ac13fdffbc 100644 --- a/pom.xml +++ b/pom.xml @@ -834,4 +834,5 @@ + From fc00efd7047d01c5fec4254549588d7dcfdf33d7 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Wed, 11 Jan 2023 14:30:19 +0800 Subject: [PATCH 5/6] Fix test cases for service name mapping (#11280) --- .../src/test/java/org/apache/dubbo/config/ServiceConfigTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java index 0e676ba427..8adbc61a15 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java @@ -544,6 +544,7 @@ class ServiceConfigTest { FrameworkModel frameworkModel = new FrameworkModel(); ApplicationModel applicationModel = frameworkModel.newApplication(); ServiceConfig serviceConfig = new ServiceConfig<>(applicationModel.newModule()); + serviceConfig.exported(); ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); AtomicInteger count = new AtomicInteger(0); ServiceNameMapping serviceNameMapping = new ServiceNameMapping() { From 67993e168e4c152fca7dfe80a047ed625d87ff4d Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Wed, 11 Jan 2023 15:01:16 +0800 Subject: [PATCH 6/6] Switch default check level (#11279) --- .../common/constants/LoggerCodeConstants.java | 2 + .../utils/AllowClassNotifyListener.java | 2 +- .../utils/SerializeSecurityManager.java | 18 +++-- .../fastjson2/Fastjson2SecurityManager.java | 70 +++++++++++-------- .../hessian2/Hessian2AllowClassManager.java | 17 +++-- 5 files changed, 66 insertions(+), 43 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java index f033d3941a..506ddadd32 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java @@ -291,6 +291,8 @@ public interface LoggerCodeConstants { String PROTOCOL_FAILED_DECODE = "4-20"; + String PROTOCOL_UNTRUSTED_SERIALIZE_CLASS = "4-21"; + // Config module String CONFIG_FAILED_CONNECT_REGISTRY = "5-1"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java index 20e37b62ee..2346e0dc2c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AllowClassNotifyListener.java @@ -20,7 +20,7 @@ import java.util.Set; public interface AllowClassNotifyListener { - SerializeCheckStatus DEFAULT_STATUS = SerializeCheckStatus.STRICT; + SerializeCheckStatus DEFAULT_STATUS = SerializeCheckStatus.WARN; void notify(SerializeCheckStatus status, Set prefixList); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java index 2dd3432765..6cea23a58d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityManager.java @@ -16,10 +16,6 @@ */ package org.apache.dubbo.common.utils; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.rpc.model.FrameworkModel; - import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; @@ -36,12 +32,18 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.model.FrameworkModel; + import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_ALLOW_LIST_FILE_PATH; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED; public class SerializeSecurityManager { private final Set allowedPrefix = new LinkedHashSet<>(); - private final static Logger logger = LoggerFactory.getLogger(SerializeSecurityManager.class); + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeSecurityManager.class); private final SerializeClassChecker checker = SerializeClassChecker.getInstance(); @@ -59,6 +61,7 @@ public class SerializeSecurityManager { .collect(Collectors.toList()); for (URL u : urls) { try { + logger.info("Read serialize allow list from " + u); String[] lines = IOUtils.readLines(u.openStream()); for (String line : lines) { line = line.trim(); @@ -68,11 +71,12 @@ public class SerializeSecurityManager { allowedPrefix.add(line); } } catch (IOException e) { - logger.error("Failed to load allow class list! Will ignore allow lis from " + u, e); + logger.error(COMMON_IO_EXCEPTION, "", "", "Failed to load allow class list! Will ignore allow lis from " + u, e); } } } catch (InterruptedException e) { - logger.error("Failed to load allow class list! Will ignore allow list from configuration.", e); + logger.error(INTERNAL_INTERRUPTED, "", "", "Failed to load allow class list! Will ignore allow list from configuration.", e); + Thread.currentThread().interrupt(); } } diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java index 7259a0630d..e9334278c8 100644 --- a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java +++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2SecurityManager.java @@ -16,7 +16,11 @@ */ package org.apache.dubbo.common.serialize.fastjson2; -import org.apache.dubbo.common.logger.Logger; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.AllowClassNotifyListener; import org.apache.dubbo.common.utils.ConcurrentHashSet; @@ -28,18 +32,16 @@ import com.alibaba.fastjson2.filter.ContextAutoTypeBeforeHandler; import com.alibaba.fastjson2.filter.Filter; import com.alibaba.fastjson2.util.TypeUtils; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import static com.alibaba.fastjson2.util.TypeUtils.loadClass; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNTRUSTED_SERIALIZE_CLASS; +import static org.apache.dubbo.common.utils.SerializeCheckStatus.STRICT; public class Fastjson2SecurityManager implements AllowClassNotifyListener { private Filter securityFilter = new Handler(AllowClassNotifyListener.DEFAULT_STATUS, new String[0]); - private final static Logger logger = LoggerFactory.getLogger(Fastjson2SecurityManager.class); + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Fastjson2SecurityManager.class); - private final static Set warnedClasses = new ConcurrentHashSet<>(1); + private static final Set warnedClasses = new ConcurrentHashSet<>(1); public Fastjson2SecurityManager(FrameworkModel frameworkModel) { SerializeSecurityManager securityManager = frameworkModel.getBeanFactory().getOrRegisterBean(SerializeSecurityManager.class); @@ -65,28 +67,40 @@ public class Fastjson2SecurityManager implements AllowClassNotifyListener { @Override public Class apply(String typeName, Class expectClass, long features) { - switch (status) { - case STRICT: - return super.apply(typeName, expectClass, features); - case WARN: - Class tryLoad = super.apply(typeName, expectClass, features); - if (tryLoad != null) { - return tryLoad; - } - case DISABLED: - Class localClass = loadClassDirectly(typeName); - if (localClass != null) { - if (status == SerializeCheckStatus.WARN && warnedClasses.add(typeName)) { - logger.error("[Serialization Security] Serialized class " + localClass.getName() + " is not in allow list. " + - "Current mode is `WARN`, will allow to deserialize it by default. " + - "Dubbo will set to `STRICT` mode by default in the future. " + - "Please add it into security/serialize.allowlist or follow FAQ to configure it."); - } - return localClass; - } - default: - return null; + Class tryLoad = super.apply(typeName, expectClass, features); + + // 1. in allow list, return + if (tryLoad != null) { + return tryLoad; } + + // 2. check if in strict mode + if (status == STRICT) { + String msg = "[Serialization Security] Serialized class " + typeName + " is not in allow list. " + + "Current mode is `STRICT`, will disallow to deserialize it by default. " + + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; + if (warnedClasses.add(typeName)) { + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); + } + + return null; + } + + // 3. try load + Class localClass = loadClassDirectly(typeName); + if (localClass != null) { + if (status == SerializeCheckStatus.WARN && warnedClasses.add(typeName)) { + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", + "[Serialization Security] Serialized class " + localClass.getName() + " is not in allow list. " + + "Current mode is `WARN`, will allow to deserialize it by default. " + + "Dubbo will set to `STRICT` mode by default in the future. " + + "Please add it into security/serialize.allowlist or follow FAQ to configure it."); + } + return localClass; + } + + // 4. class not found + return null; } public Class loadClassDirectly(String typeName) { diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java index 906b5878df..a0564d4f66 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2AllowClassManager.java @@ -16,7 +16,10 @@ */ package org.apache.dubbo.common.serialize.hessian2; -import org.apache.dubbo.common.logger.Logger; +import java.util.Arrays; +import java.util.Set; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.AllowClassNotifyListener; import org.apache.dubbo.common.utils.ConcurrentHashSet; @@ -24,8 +27,7 @@ import org.apache.dubbo.common.utils.SerializeCheckStatus; import org.apache.dubbo.common.utils.SerializeSecurityManager; import org.apache.dubbo.rpc.model.FrameworkModel; -import java.util.Arrays; -import java.util.Set; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNTRUSTED_SERIALIZE_CLASS; /** * Inspired by Fastjson2 @@ -34,9 +36,9 @@ import java.util.Set; public class Hessian2AllowClassManager implements AllowClassNotifyListener { private static final long MAGIC_HASH_CODE = 0xcbf29ce484222325L; private static final long MAGIC_PRIME = 0x100000001b3L; - private static final Logger logger = LoggerFactory.getLogger(Hessian2AllowClassManager.class); + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Hessian2AllowClassManager.class); private volatile SerializeCheckStatus checkStatus = AllowClassNotifyListener.DEFAULT_STATUS; - private final static Set warnedClasses = new ConcurrentHashSet<>(1); + private static final Set warnedClasses = new ConcurrentHashSet<>(1); private volatile long[] allowPrefixes = new long[0]; public Hessian2AllowClassManager(FrameworkModel frameworkModel) { @@ -99,14 +101,15 @@ public class Hessian2AllowClassManager implements AllowClassNotifyListener { "Current mode is `STRICT`, will disallow to deserialize it by default. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (warnedClasses.add(className)) { - logger.error(msg); + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } throw new IllegalArgumentException(msg); } else { Class clazz = Class.forName(className, false, classLoader); if (warnedClasses.add(className)) { - logger.error("[Serialization Security] Serialized class " + clazz.getName() + " is not in allow list. " + + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", + "[Serialization Security] Serialized class " + clazz.getName() + " is not in allow list. " + "Current mode is `WARN`, will allow to deserialize it by default. " + "Dubbo will set to `STRICT` mode by default in the future. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it.");