Apache ShenYu missing authentication allows gateway registration
Description
Apache ShenYu Admin 2.4.0-2.4.1 lacks authentication on HTTP gateway registration, enabling unauthorized gateway registration and potential traffic manipulation.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache ShenYu Admin 2.4.0-2.4.1 lacks authentication on HTTP gateway registration, enabling unauthorized gateway registration and potential traffic manipulation.
Vulnerability
Apache ShenYu Admin versions 2.4.0 and 2.4.1 [2] lack authentication for HTTP-based gateway registration. The /register endpoint does not verify the identity of the registering gateway, allowing any client to register as a gateway.
Exploitation
If ShenYu Admin is exposed to the internet [4], an attacker can send a crafted HTTP request to the registration endpoint without any authentication. No user interaction or special privileges are required.
Impact
Successful exploitation allows an attacker to register as a gateway, potentially intercepting or manipulating traffic [1]. This could lead to data disclosure or unauthorized API governance actions.
Mitigation
Upgrade to Apache ShenYu 2.4.2 or apply the patch from commit 9a02215 [3] [4]. If upgrade is not possible, restrict network access to ShenYu Admin.
- GitHub - apache/shenyu: Apache ShenYu is a Java native API Gateway for service proxy, protocol conversion and API governance.
- NVD - CVE-2022-23945
- [type:refactor] add an authentication on shenyu admin when register b… · apache/shenyu@9a02215
- security - CVE-2022-23945: Apache ShenYu (incubating) missing authentication allows gateway registration
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.shenyu:shenyu-commonMaven | >= 2.4.0, < 2.4.2 | 2.4.2 |
Affected products
2Patches
19a0221501303[type:refactor] add an authentication on shenyu admin when register by http (#2723)
40 files changed · +627 −56
shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/bean/StatelessAuthFilter.java+2 −3 modified@@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.shenyu.admin.model.result.ShenyuAdminResult; import org.apache.shenyu.admin.utils.ShenyuResultMessage; +import org.apache.shenyu.common.constant.Constants; import org.apache.shenyu.common.exception.CommonErrorCode; import org.apache.shenyu.common.utils.GsonUtils; import org.apache.shiro.subject.Subject; @@ -41,8 +42,6 @@ public class StatelessAuthFilter extends AccessControlFilter { private static final Logger LOG = LoggerFactory.getLogger(StatelessAuthFilter.class); - private static final String HEAD_TOKEN = "X-Access-Token"; - @Override protected boolean isAccessAllowed(final ServletRequest servletRequest, final ServletResponse servletResponse, @@ -58,7 +57,7 @@ protected boolean onAccessDenied(final ServletRequest servletRequest, return true; } - String tokenValue = httpServletRequest.getHeader(HEAD_TOKEN); + String tokenValue = httpServletRequest.getHeader(Constants.X_ACCESS_TOKEN); if (StringUtils.isBlank(tokenValue)) { LOG.error("token is null."); unionFailResponse(servletResponse);
shenyu-admin/src/main/resources/application.yml+0 −2 modified@@ -88,8 +88,6 @@ shenyu: - /index** - /platform/login - /websocket - - /configs/** - - /shenyu-client/** - /error - /actuator/health - /swagger-ui.html
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShenyuClientShutdownHook.java+7 −4 modified@@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; + import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig; import org.slf4j.Logger; @@ -49,10 +50,11 @@ public class ShenyuClientShutdownHook { private static IdentityHashMap<Thread, Thread> delayedHooks = new IdentityHashMap<>(); - public ShenyuClientShutdownHook() { } + public ShenyuClientShutdownHook() { + } public ShenyuClientShutdownHook(final ShenyuClientRegisterRepository repository, final ShenyuRegisterCenterConfig config) { - String name = hookNamePrefix + "-" + hookId.incrementAndGet(); + String name = String.join("-", hookNamePrefix, String.valueOf(hookId.incrementAndGet())); Runtime.getRuntime().addShutdownHook(new Thread(repository::close, name)); LOG.info("Add hook {}", name); ShenyuClientShutdownHook.props = config.getProps(); @@ -65,7 +67,7 @@ public ShenyuClientShutdownHook(final ShenyuClientRegisterRepository repository, * @param props Properties */ public static void set(final ShenyuClientRegisterRepository result, final Properties props) { - String name = hookNamePrefix + "-" + hookId.incrementAndGet(); + String name = String.join("-", hookNamePrefix, String.valueOf(hookId.incrementAndGet())); Runtime.getRuntime().addShutdownHook(new Thread(result::close, name)); LOG.info("Add hook {}", name); ShenyuClientShutdownHook.props = props; @@ -113,7 +115,8 @@ public void run() { LOG.info("sleep {}ms", shutdownWaitTime); try { TimeUnit.MILLISECONDS.sleep(shutdownWaitTime); - } catch (InterruptedException ignore) { } + } catch (InterruptedException ignore) { + } hook.run(); }, hook.getName()); delayHooks.put(delayHook, delayHook);
shenyu-client/shenyu-client-http/shenyu-client-springcloud/src/test/java/org/apache/shenyu/client/springcloud/init/SpringCloudClientBeanPostProcessorTest.java+17 −6 modified@@ -37,6 +37,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Optional; import java.util.Properties; import static org.hamcrest.Matchers.equalTo; @@ -51,9 +52,12 @@ @RunWith(MockitoJUnitRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public final class SpringCloudClientBeanPostProcessorTest { + @Mock private static Environment env; + private MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + private final SpringCloudClientTestBean springCloudClientTestBean = new SpringCloudClientTestBean(); @Before @@ -63,27 +67,31 @@ public void init() { @Test public void testShenyuBeanProcess() { + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token")); // config with full SpringCloudClientBeanPostProcessor springCloudClientBeanPostProcessor = buildSpringCloudClientBeanPostProcessor(true); assertThat(springCloudClientTestBean, equalTo(springCloudClientBeanPostProcessor.postProcessAfterInitialization(springCloudClientTestBean, "springCloudClientTestBean"))); + registerUtilsMockedStatic.close(); } @Test public void testNormalBeanProcess() { + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token")); SpringCloudClientBeanPostProcessor springCloudClientBeanPostProcessor = buildSpringCloudClientBeanPostProcessor(false); Object normalBean = new Object(); assertThat(normalBean, equalTo(springCloudClientBeanPostProcessor.postProcessAfterInitialization(normalBean, "normalBean"))); + registerUtilsMockedStatic.close(); } @Test public void testWithShenyuClientAnnotation() { - try (MockedStatic mocked = mockStatic(RegisterUtils.class)) { - mocked.when(() -> RegisterUtils.doRegister(any(), any(), any())) - .thenAnswer((Answer<Void>) invocation -> null); - SpringCloudClientBeanPostProcessor springCloudClientBeanPostProcessor = buildSpringCloudClientBeanPostProcessor(false); - assertThat(springCloudClientTestBean, equalTo(springCloudClientBeanPostProcessor.postProcessAfterInitialization(springCloudClientTestBean, "normalBean"))); - } + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token")); + registerUtilsMockedStatic.when(() -> RegisterUtils.doRegister(any(), any(), any())) + .thenAnswer((Answer<Void>) invocation -> null); + SpringCloudClientBeanPostProcessor springCloudClientBeanPostProcessor = buildSpringCloudClientBeanPostProcessor(false); + assertThat(springCloudClientTestBean, equalTo(springCloudClientBeanPostProcessor.postProcessAfterInitialization(springCloudClientTestBean, "normalBean"))); + registerUtilsMockedStatic.close(); } private SpringCloudClientBeanPostProcessor buildSpringCloudClientBeanPostProcessor(final boolean full) { @@ -92,11 +100,14 @@ private SpringCloudClientBeanPostProcessor buildSpringCloudClientBeanPostProcess properties.setProperty("isFull", full + ""); properties.setProperty("ip", "127.0.0.1"); properties.setProperty("port", "8081"); + properties.setProperty("username", "admin"); + properties.setProperty("password", "123456"); PropertiesConfig config = new PropertiesConfig(); config.setProps(properties); ShenyuRegisterCenterConfig mockRegisterCenter = new ShenyuRegisterCenterConfig(); mockRegisterCenter.setServerLists("http://127.0.0.1:8080"); mockRegisterCenter.setRegisterType("http"); + mockRegisterCenter.setProps(properties); return new SpringCloudClientBeanPostProcessor(config, ShenyuClientRegisterRepositoryFactory.newInstance(mockRegisterCenter), env); }
shenyu-client/shenyu-client-tars/src/test/java/org/apache/shenyu/client/tars/TarsServiceBeanPostProcessorTest.java+12 −0 modified@@ -21,17 +21,23 @@ import org.apache.shenyu.client.core.register.ShenyuClientRegisterRepositoryFactory; import org.apache.shenyu.client.tars.common.annotation.ShenyuTarsClient; import org.apache.shenyu.client.tars.common.annotation.ShenyuTarsService; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.apache.shenyu.register.common.config.PropertiesConfig; import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; +import org.mockito.MockedStatic; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Optional; import java.util.Properties; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; + /** * Test case for {@link TarsServiceBeanPostProcessor}. */ @@ -46,14 +52,20 @@ public static void init() { properties.setProperty("contextPath", "/tars"); properties.setProperty("port", "8080"); properties.setProperty("host", "localhost"); + properties.setProperty("username", "admin"); + properties.setProperty("password", "123456"); PropertiesConfig config = new PropertiesConfig(); config.setProps(properties); ShenyuRegisterCenterConfig mockRegisterCenter = new ShenyuRegisterCenterConfig(); mockRegisterCenter.setServerLists("http://localhost:58080"); mockRegisterCenter.setRegisterType("http"); + mockRegisterCenter.setProps(properties); + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token")); tarsServiceBeanPostProcessor = new TarsServiceBeanPostProcessor(config, ShenyuClientRegisterRepositoryFactory.newInstance(mockRegisterCenter)); + registerUtilsMockedStatic.close(); } @Test
shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java+75 −0 modified@@ -487,6 +487,81 @@ public interface Constants { */ String TRANSMIT_HEADER_TO_GENERAL_CONTEXT_TYPE = "transmitHeaderToGeneralContext"; + /** + * When register by http, the meta register path. + */ + String META_PATH = "/shenyu-client/register-metadata"; + + /** + * When register by http, the meta type. + */ + String META_TYPE = "metadata"; + + /** + * When register by http, the uri path. + */ + String URI_PATH = "/shenyu-client/register-uri"; + + /** + * When register by http, the login path. + */ + String LOGIN_PATH = "/platform/login"; + + /** + * When register by http, admin username. + */ + String USER_NAME = "username"; + + /** + * Login name. + */ + String LOGIN_NAME = "userName"; + + /** + * When register by http, admin password. + */ + String PASS_WORD = "password"; + + /** + * X-Access-Token. + */ + String X_ACCESS_TOKEN = "X-Access-Token"; + + /** + * The admin return result code. + */ + String ADMIN_RESULT_CODE = "code"; + + /** + * The admin return result data. + */ + String ADMIN_RESULT_DATA = "data"; + + /** + * The admin return result token. + */ + String ADMIN_RESULT_TOKEN = "token"; + + /** + * The admin userName. + */ + String ADMIN_RESULT_USERNAME = "userName"; + + /** + * The admin password. + */ + String ADMIN_RESULT_PASSWORD = "password"; + + /** + * shenyu admin path configs fetch. + */ + String SHENYU_ADMIN_PATH_CONFIGS_FETCH = "/configs/fetch"; + + /** + * shenyu admin path configs listener. + */ + String SHENYU_ADMIN_PATH_CONFIGS_LISTENER = "/configs/listener"; + /** * String q. */
shenyu-common/src/main/java/org/apache/shenyu/common/utils/FreshBeanHolder.java+73 −0 added@@ -0,0 +1,73 @@ +/* + * 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.shenyu.common.utils; + +import java.util.function.Function; + +public class FreshBeanHolder<E, O> implements Function<E, O> { + + private final Function<E, O> function; + + private volatile O o; + + public FreshBeanHolder(final Function<E, O> function) { + this.function = function; + } + + /** + * Apply. + * + * @param e e + * @return O o + */ + @Override + public O apply(final E e) { + if (o != null) { + return o; + } + return init(e); + } + + /** + * Init. + * + * @return bean + */ + synchronized O init(final E e) { + if (o != null) { + return o; + } + O res = function.apply(e); + o = res; + return res; + } + + /** + * Fresh. + * + * @param e e + */ + public void doFresh(final E e) { + O fresh = function.apply(e); + if (fresh != null) { + synchronized (this) { + this.o = fresh; + } + } + } +}
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-alibaba-dubbo-service-annotation/src/main/resources/application.yml+2 −0 modified@@ -27,6 +27,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: dubbo: props:
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-alibaba-dubbo-service/src/main/resources/application.yml+2 −0 modified@@ -29,6 +29,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: dubbo: props:
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service-annotation/src/main/resources/application.yml+3 −0 modified@@ -26,6 +26,9 @@ shenyu: register: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 + props: + username: admin + password: 123456 client: dubbo: props:
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service/src/main/resources/application.yml+3 −0 modified@@ -29,6 +29,9 @@ shenyu: register: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 + props: + username: admin + password: 123456 client: dubbo: props:
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service-xml/src/main/resources/shenyu.xml+6 −0 modified@@ -28,6 +28,12 @@ <bean id="shenyuRegisterCenterConfig" class="org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig"> <property name="registerType" value="http"/> <property name="serverLists" value="http://localhost:9095"/> + <property name="props"> + <props> + <prop key="username">admin</prop> + <prop key="password">123456</prop> + </props> + </property> </bean> <!-- ClientPropertiesConfig -->
shenyu-examples/shenyu-examples-dubbo/shenyu-examples-spring-cloud-alibaba-dubbo-service-annotation/src/main/resources/application.yml+3 −0 modified@@ -28,6 +28,9 @@ shenyu: register: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 + props: + username: admin + password: 123456 client: dubbo: props:
shenyu-examples/shenyu-examples-grpc/src/main/resources/application.yml+2 −0 modified@@ -29,6 +29,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: grpc: props:
shenyu-examples/shenyu-examples-http/src/main/resources/application.yml+2 −0 modified@@ -22,6 +22,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: http: props:
shenyu-examples/shenyu-examples-https/src/main/resources/application.yml+2 −0 modified@@ -28,6 +28,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://shenyu-admin:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: http: props:
shenyu-examples/shenyu-examples-motan/shenyu-examples-motan-service/src/main/resources/application.yml+2 −0 modified@@ -28,6 +28,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: motan: props:
shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/src/main/resources/application.yml+2 −0 modified@@ -35,6 +35,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: sofa: props:
shenyu-examples/shenyu-examples-springcloud/src/main/resources/application.yml+2 −0 modified@@ -41,6 +41,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: springCloud: props:
shenyu-examples/shenyu-examples-tars/src/main/resources/application.yml+2 −0 modified@@ -33,6 +33,8 @@ shenyu: registerType: http #zookeeper #etcd #nacos #consul serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848 props: + username: admin + password: 123456 client: tars: props:
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java+2 −2 modified@@ -128,13 +128,13 @@ private Boolean filterRule(final RuleData ruleData, final ServerWebExchange exch } private void selectorLog(final SelectorData selectorData, final String pluginName) { - if (Boolean.TRUE.equals(selectorData.getLogged())) { + if (selectorData.getLogged()) { LOG.info("{} selector success match , selector name :{}", pluginName, selectorData.getName()); } } private void ruleLog(final RuleData ruleData, final String pluginName) { - if (Boolean.TRUE.equals(ruleData.getLoged())) { + if (ruleData.getLoged()) { LOG.info("{} rule success match , rule name :{}", pluginName, ruleData.getName()); } }
shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/HttpClientRegisterRepository.java+28 −12 modified@@ -29,8 +29,8 @@ import org.apache.shenyu.spi.Join; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.util.List; +import java.util.Optional; /** * The type Http client register repository. @@ -39,45 +39,61 @@ public class HttpClientRegisterRepository implements ShenyuClientRegisterRepository { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterUtils.class); - - private static final String META_PATH = "/shenyu-client/register-metadata"; - private static final String META_TYPE = "metadata"; + private String username; - private static final String URI_PATH = "/shenyu-client/register-uri"; + private String password; private List<String> serverList; - public HttpClientRegisterRepository() { } + private String accessToken; + + public HttpClientRegisterRepository() { + } public HttpClientRegisterRepository(final ShenyuRegisterCenterConfig config) { init(config); } @Override public void init(final ShenyuRegisterCenterConfig config) { + this.username = config.getProps().getProperty(Constants.USER_NAME); + this.password = config.getProps().getProperty(Constants.PASS_WORD); this.serverList = Lists.newArrayList(Splitter.on(",").split(config.getServerLists())); + this.getAccessToken().ifPresent(V -> this.accessToken = String.valueOf(V)); } - + /** * Persist uri. * * @param registerDTO the register dto */ @Override public void persistURI(final URIRegisterDTO registerDTO) { - doRegister(registerDTO, URI_PATH, Constants.URI); + doRegister(registerDTO, Constants.URI_PATH, Constants.URI); } - + @Override public void persistInterface(final MetaDataRegisterDTO metadata) { - doRegister(metadata, META_PATH, META_TYPE); + doRegister(metadata, Constants.META_PATH, Constants.META_TYPE); } - + + private Optional getAccessToken() { + for (String server : serverList) { + try { + Optional login = RegisterUtils.doLogin(username, password, server.concat(Constants.LOGIN_PATH)); + return login; + } catch (Exception e) { + LOGGER.error("login admin url :{} is fail, will retry, ex is :{}", server, e); + } + } + return Optional.empty(); + } + private <T> void doRegister(final T t, final String path, final String type) { for (String server : serverList) { try { - RegisterUtils.doRegister(GsonUtils.getInstance().toJson(t), server + path, type); + RegisterUtils.doRegister(GsonUtils.getInstance().toJson(t), server.concat(path), type, accessToken); return; } catch (Exception e) { LOGGER.error("register admin url :{} is fail, will retry, ex is :{}", server, e);
shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/utils/OkHttpTools.java+39 −0 modified@@ -21,8 +21,11 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; +import okhttp3.Headers; +import okhttp3.HttpUrl; import java.io.IOException; +import java.util.Map; import java.util.concurrent.TimeUnit; /** @@ -72,4 +75,40 @@ public String post(final String url, final String json) throws IOException { .build(); return client.newCall(request).execute().body().string(); } + + /** + * Post string. + * + * @param url the url + * @param json the json + * @param headers the headers + * @return the string + * @throws IOException the io exception + */ + public String post(final String url, final String json, final Headers headers) throws IOException { + RequestBody body = RequestBody.create(JSON, json); + Request request = new Request.Builder() + .headers(headers) + .url(url) + .post(body) + .build(); + return client.newCall(request).execute().body().string(); + } + + /** + * Get string. + * + * @param url the url + * @param query the query + * @return the http result + * @throws IOException the io exception + */ + public String get(final String url, final Map<String, Object> query) throws IOException { + Request.Builder reqBuild = new Request.Builder(); + HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); + query.forEach((K, V) -> urlBuilder.addQueryParameter(K, String.valueOf(V))); + reqBuild.url(urlBuilder.build()); + Request request = reqBuild.build(); + return client.newCall(request).execute().body().string(); + } }
shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/main/java/org/apache/shenyu/register/client/http/utils/RegisterUtils.java+57 −2 modified@@ -17,11 +17,19 @@ package org.apache.shenyu.register.client.http.utils; +import okhttp3.Headers; +import org.apache.shenyu.common.constant.Constants; +import org.apache.shenyu.common.exception.CommonErrorCode; +import org.apache.shenyu.common.utils.GsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import static org.apache.shenyu.common.constant.Constants.SUCCESS; @@ -34,12 +42,35 @@ public final class RegisterUtils { private RegisterUtils() { } - + + /** + * Do register. + * + * @param json the json + * @param url the url + * @param type the type + * @param accessToken the token + * @throws IOException the io exception + */ + public static void doRegister(final String json, final String url, final String type, final String accessToken) throws IOException { + if (StringUtils.isEmpty(accessToken)) { + LOGGER.error("{} client register error accessToken is null, please check the config : {} ", type, json); + return; + } + Headers headers = new Headers.Builder().add(Constants.X_ACCESS_TOKEN, accessToken).build(); + String result = OkHttpTools.getInstance().post(url, json, headers); + if (Objects.equals(SUCCESS, result)) { + LOGGER.info("{} client register success: {} ", type, json); + } else { + LOGGER.error("{} client register error: {} ", type, json); + } + } + /** * Do register. * * @param json the json - * @param url the url + * @param url the url * @param type the type * @throws IOException the io exception */ @@ -51,4 +82,28 @@ public static void doRegister(final String json, final String url, final String LOGGER.error("{} client register error: {} ", type, json); } } + + /** + * Do login. + * + * @param username the username + * @param password the password + * @param url the ulr + * @return Optional token + * @throws IOException the io exception + */ + public static Optional doLogin(final String username, final String password, final String url) throws IOException { + Map<String, Object> loginMap = new HashMap<>(2); + loginMap.put(Constants.LOGIN_NAME, username); + loginMap.put(Constants.PASS_WORD, password); + String result = OkHttpTools.getInstance().get(url, loginMap); + Map<String, Object> resultMap = GsonUtils.getInstance().convertToMap(result); + if (!String.valueOf(CommonErrorCode.SUCCESSFUL).equals(String.valueOf(resultMap.get(Constants.ADMIN_RESULT_CODE)))) { + return Optional.empty(); + } + String tokenJson = GsonUtils.getInstance().toJson(resultMap.get(Constants.ADMIN_RESULT_DATA)); + LOGGER.info("login success: {} ", tokenJson); + Map<String, Object> tokenMap = GsonUtils.getInstance().convertToMap(tokenJson); + return Optional.ofNullable(tokenMap.get(Constants.ADMIN_RESULT_TOKEN)); + } }
shenyu-register-center/shenyu-register-client/shenyu-register-client-http/src/test/java/org/apache/shenyu/register/client/http/RegisterUtilsTest.java+11 −9 modified@@ -18,9 +18,11 @@ package org.apache.shenyu.register.client.http; import com.google.gson.Gson; + import java.io.IOException; import java.util.HashMap; import java.util.Map; + import org.apache.shenyu.register.client.http.utils.OkHttpTools; import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.apache.shenyu.register.common.enums.RegisterTypeEnum; @@ -39,15 +41,15 @@ * Test case for {@link RegisterUtils}. */ public final class RegisterUtilsTest { - + private Gson gson = new Gson(); - + private OkHttpTools okHttpTools; - + private String json; - + private String url; - + @Before public void setUp() { okHttpTools = mock(OkHttpTools.class); @@ -64,18 +66,18 @@ public void setUp() { json = gson.toJson(jsonMap); url = "http://localhost:9095/shenyu-client/dubbo-register"; } - + @Test public void testDoRegisterWhenSuccess() throws IOException { when(okHttpTools.post(url, json)).thenReturn("success"); - + try (MockedStatic<OkHttpTools> okHttpToolsMockedStatic = mockStatic(OkHttpTools.class)) { okHttpToolsMockedStatic.when(OkHttpTools::getInstance).thenReturn(okHttpTools); RegisterUtils.doRegister(json, url, RegisterTypeEnum.DUBBO.getName()); verify(okHttpTools, times(1)).post(eq(url), eq(json)); } } - + @Test public void testDoRegisterWhenError() throws IOException { when(okHttpTools.post(url, json)).thenReturn("Error parameter!"); @@ -85,7 +87,7 @@ public void testDoRegisterWhenError() throws IOException { verify(okHttpTools, times(1)).post(eq(url), eq(json)); } } - + @Test(expected = IOException.class) public void testDoRegisterWhenThrowException() throws IOException { when(okHttpTools.post(url, json)).thenThrow(IOException.class);
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-alibaba-dubbo/src/test/java/org/apache/shenyu/springboot/starter/client/alibaba/dubbo/ShenyuAlibabaDubboClientConfigurationTest.java+9 −0 modified@@ -18,14 +18,20 @@ package org.apache.shenyu.springboot.starter.client.alibaba.dubbo; import org.apache.shenyu.client.alibaba.dubbo.AlibabaDubboServiceBeanListener; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuAlibabaDubboClientConfiguration}. @@ -37,6 +43,8 @@ public class ShenyuAlibabaDubboClientConfigurationTest { @Test public void testShenyuAlibabaDubboClientConfiguration() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ShenyuAlibabaDubboClientConfiguration.class)) .withBean(ShenyuAlibabaDubboClientConfigurationTest.class) @@ -47,5 +55,6 @@ public void testShenyuAlibabaDubboClientConfiguration() { assertNotNull(listener); } ); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-alibaba-dubbo/src/test/resources/application.properties+2 −0 modified@@ -18,6 +18,8 @@ dubbo.registry.address=zookeeper://localhost:2181 # more see shenyu-register-center module shenyu.register.register-type=http shenyu.register.serverLists=http://localhost:9095 +shenyu.register.props.username=admin +shenyu.register.props.password=123456 # more see org.apache.shenyu.client.core.shutdown.ShenyuClientShutdownHook.TakeoverOtherHooksThread.class shenyu.register.props[shutdownWaitTime]=3000
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-apache-dubbo/src/test/java/org/apache/shenyu/springboot/starter/client/apache/dubbo/ShenyuApacheDubboClientConfigurationTest.java+9 −0 modified@@ -18,14 +18,20 @@ package org.apache.shenyu.springboot.starter.client.apache.dubbo; import org.apache.shenyu.client.apache.dubbo.ApacheDubboServiceBeanListener; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuApacheDubboClientConfiguration}. @@ -37,6 +43,8 @@ public class ShenyuApacheDubboClientConfigurationTest { @Test public void testShenyuApacheDubboClientConfiguration() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ShenyuApacheDubboClientConfiguration.class)) .withBean(ShenyuApacheDubboClientConfigurationTest.class) @@ -47,5 +55,6 @@ public void testShenyuApacheDubboClientConfiguration() { assertNotNull(listener); } ); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-apache-dubbo/src/test/resources/application.properties+2 −0 modified@@ -18,6 +18,8 @@ dubbo.registry.address=zookeeper://localhost:2181 # more see shenyu-register-center module shenyu.register.register-type=http shenyu.register.serverLists=http://localhost:9095 +shenyu.register.props.username=admin +shenyu.register.props.password=123456 # more see org.apache.shenyu.client.core.shutdown.ShenyuClientShutdownHook.TakeoverOtherHooksThread.class shenyu.register.props[shutdownWaitTime]=3000
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-common/src/test/java/org/apache/shenyu/springboot/starter/client/common/config/ShenyuClientCommonBeanConfigurationTest.java+17 −0 modified@@ -18,18 +18,24 @@ package org.apache.shenyu.springboot.starter.client.common.config; import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.apache.shenyu.register.common.config.ShenyuClientConfig; import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig; import org.apache.shenyu.register.common.enums.RegisterTypeEnum; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuClientCommonBeanConfiguration}. @@ -49,6 +55,8 @@ public void before() { "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.dubbo.props[contextPath]=/common", "shenyu.client.dubbo.props[appName]=common", "shenyu.client.dubbo.props[host]=localhost", @@ -58,27 +66,36 @@ public void before() { @Test public void testShenyuClientRegisterRepository() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { ShenyuClientRegisterRepository repository = context.getBean("shenyuClientRegisterRepository", ShenyuClientRegisterRepository.class); assertNotNull(repository); }); + registerUtilsMockedStatic.close(); } @Test public void testShenyuRegisterCenterConfig() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { ShenyuRegisterCenterConfig config = context.getBean("shenyuRegisterCenterConfig", ShenyuRegisterCenterConfig.class); assertNotNull(config); assertThat(config.getRegisterType()).isEqualTo(RegisterTypeEnum.HTTP.getName()); }); + registerUtilsMockedStatic.close(); } @Test public void testShenyuClientConfig() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { ShenyuClientConfig config = context.getBean("shenyuClientConfig", ShenyuClientConfig.class); assertNotNull(config); assertThat(config.getClient()).containsKey("dubbo"); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-grpc/src/test/java/org/apache/dromara/springboot/starter/client/grpc/ShenyuGrpcClientConfigurationTest.java+17 −0 modified@@ -21,15 +21,21 @@ import org.apache.shenyu.client.grpc.GrpcClientBeanPostProcessor; import org.apache.shenyu.client.grpc.GrpcContextRefreshedEventListener; import org.apache.shenyu.client.grpc.server.GrpcServerRunner; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuGrpcClientConfiguration}. @@ -50,6 +56,8 @@ public void before() { "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.grpc.props[contextPath]=/grpc", "shenyu.client.grpc.props[appName]=grpc", "shenyu.client.grpc.props[ipAndPort]=127.0.0.1:8080", @@ -59,25 +67,34 @@ public void before() { @Test public void testGrpcClientBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { GrpcClientBeanPostProcessor processor = context.getBean("grpcServiceBeanPostProcessor", GrpcClientBeanPostProcessor.class); assertNotNull(processor); }); + registerUtilsMockedStatic.close(); } @Test public void testGrpcContextRefreshedEventListener() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { GrpcContextRefreshedEventListener listener = context.getBean("grpcContextRefreshedEventListener", GrpcContextRefreshedEventListener.class); assertNotNull(listener); }); + registerUtilsMockedStatic.close(); } @Test public void testGrpcServerRunner() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { GrpcServerRunner runner = context.getBean("grpcServer", GrpcServerRunner.class); assertNotNull(runner); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-motan/src/test/java/org/apache/shenyu/springboot/starter/client/motan/ShenyuMotanClientConfigurationTest.java+11 −0 modified@@ -19,13 +19,19 @@ package org.apache.shenyu.springboot.starter.client.motan; import org.apache.shenyu.client.motan.MotanServiceBeanPostProcessor; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuMotanClientConfiguration}. @@ -36,13 +42,17 @@ public class ShenyuMotanClientConfigurationTest { @Test public void testMotanServiceBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ShenyuMotanClientConfiguration.class)) .withBean(ShenyuMotanClientConfigurationTest.class) .withPropertyValues( "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.motan.props[contextPath]=/motan", "shenyu.client.motan.props[appName]=motan", "shenyu.client.motan.props[host]=127.0.0.1", @@ -52,5 +62,6 @@ public void testMotanServiceBeanPostProcessor() { MotanServiceBeanPostProcessor processor = context.getBean("tarsServiceBeanPostProcessor", MotanServiceBeanPostProcessor.class); assertNotNull(processor); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-sofa/src/test/java/org/apache/shenyu/springboot/starter/client/sofa/ShenyuSofaClientConfigurationTest.java+11 −0 modified@@ -19,13 +19,19 @@ package org.apache.shenyu.springboot.starter.client.sofa; import org.apache.shenyu.client.sofa.SofaServiceBeanPostProcessor; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuSofaClientConfiguration}. @@ -36,13 +42,17 @@ public class ShenyuSofaClientConfigurationTest { @Test public void testSofaServiceBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ShenyuSofaClientConfiguration.class)) .withBean(ShenyuSofaClientConfigurationTest.class) .withPropertyValues( "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.sofa.props[contextPath]=/sofa", "shenyu.client.sofa.props[appName]=sofa", "shenyu.client.sofa.props[host]=127.0.0.1", @@ -53,5 +63,6 @@ public void testSofaServiceBeanPostProcessor() { assertNotNull(processor); } ); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springcloud/src/test/java/org/apache/shenyu/springboot/starter/client/springcloud/ShenyuSpringCloudClientConfigurationTest.java+14 −0 modified@@ -19,14 +19,20 @@ import org.apache.shenyu.client.springcloud.init.ContextRegisterListener; import org.apache.shenyu.client.springcloud.init.SpringCloudClientBeanPostProcessor; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuSpringCloudClientConfiguration}. @@ -46,6 +52,8 @@ public void before() { "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "spring.application.name=springcloud", "shenyu.client.springCloud.props[contextPath]=/springcloud", "shenyu.client.springCloud.props[port]=8884" @@ -54,17 +62,23 @@ public void before() { @Test public void testSpringCloudClientBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { SpringCloudClientBeanPostProcessor repository = context.getBean("springCloudClientBeanPostProcessor", SpringCloudClientBeanPostProcessor.class); assertNotNull(repository); }); + registerUtilsMockedStatic.close(); } @Test public void testContextRegisterListener() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { ContextRegisterListener listener = context.getBean("contextRegisterListener", ContextRegisterListener.class); assertNotNull(listener); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/test/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfigurationTest.java+14 −0 modified@@ -19,14 +19,20 @@ import org.apache.shenyu.client.springmvc.init.ContextRegisterListener; import org.apache.shenyu.client.springmvc.init.SpringMvcClientBeanPostProcessor; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuSpringMvcClientConfiguration}. @@ -46,6 +52,8 @@ public void before() { "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.http.props[contextPath]=/http", "shenyu.client.http.props[appName]=http", "shenyu.client.http.props[port]=8189" @@ -54,17 +62,23 @@ public void before() { @Test public void testSpringMvcClientBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { SpringMvcClientBeanPostProcessor processor = context.getBean("springHttpClientBeanPostProcessor", SpringMvcClientBeanPostProcessor.class); assertNotNull(processor); }); + registerUtilsMockedStatic.close(); } @Test public void testContextRegisterListener() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { ContextRegisterListener listener = context.getBean("contextRegisterListener", ContextRegisterListener.class); assertNotNull(listener); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-tars/src/test/java/org/apache/shenyu/springboot/starter/client/tars/ShenyuTarsClientConfigurationTest.java+14 −0 modified@@ -20,14 +20,20 @@ import org.apache.shenyu.client.tars.TarsContextRefreshedEventListener; import org.apache.shenyu.client.tars.TarsServiceBeanPostProcessor; +import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Configuration; +import java.util.Optional; + import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; /** * Test case for {@link ShenyuTarsClientConfiguration}. @@ -47,6 +53,8 @@ public void before() { "debug=true", "shenyu.register.registerType=http", "shenyu.register.serverLists=http://localhost:9095", + "shenyu.register.props.username=admin", + "shenyu.register.props.password=123456", "shenyu.client.tars.props[contextPath]=/tars", "shenyu.client.tars.props[appName]=tars", "shenyu.client.tars.props[host]=127.0.0.1", @@ -56,17 +64,23 @@ public void before() { @Test public void testTarsServiceBeanPostProcessor() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { TarsServiceBeanPostProcessor processor = context.getBean("tarsServiceBeanPostProcessor", TarsServiceBeanPostProcessor.class); assertNotNull(processor); }); + registerUtilsMockedStatic.close(); } @Test public void testTarsContextRefreshedEventListener() { + MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class); + registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token")); applicationContextRunner.run(context -> { TarsContextRefreshedEventListener listener = context.getBean("tarsContextRefreshedEventListener", TarsContextRefreshedEventListener.class); assertNotNull(listener); }); + registerUtilsMockedStatic.close(); } }
shenyu-spring-boot-starter/shenyu-spring-boot-starter-sync-data-center/shenyu-spring-boot-starter-sync-data-http/src/test/java/org/apache/shenyu/springboot/starter/sync/data/http/HttpClientPluginConfigurationTest.java+23 −0 modified@@ -18,6 +18,8 @@ package org.apache.shenyu.springboot.starter.sync.data.http; import com.github.tomakehurst.wiremock.WireMockServer; +import org.apache.shenyu.common.exception.CommonErrorCode; +import org.apache.shenyu.common.utils.GsonUtils; import org.apache.shenyu.sync.data.api.PluginDataSubscriber; import org.apache.shenyu.sync.data.http.HttpSyncDataService; import org.apache.shenyu.sync.data.http.config.HttpConfig; @@ -35,6 +37,8 @@ import java.nio.file.Files; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -58,6 +62,8 @@ properties = { "shenyu.sync.http.url=http://localhost:18848", "shenyu.sync.http.delayTime=3", + "shenyu.sync.http.username=admin", + "shenyu.sync.http.password=123456", "shenyu.sync.http.connectionTimeout=5" }) @EnableAutoConfiguration @@ -74,6 +80,13 @@ public final class HttpClientPluginConfigurationTest { public static void setUpWireMock() throws Exception { WireMockServer wireMockServer = new WireMockServer(options().port(18848)); + wireMockServer.stubFor(get(urlPathEqualTo("/platform/login")) + .willReturn(aResponse() + .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) + .withBody(mockLoginResponseJson()) + .withStatus(200)) + ); + wireMockServer.stubFor(get(urlPathEqualTo("/configs/fetch")) .willReturn(aResponse() .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) @@ -113,4 +126,14 @@ private static String mockConfigsFetchResponseJson() throws Exception { Paths.get(Objects.requireNonNull(HttpClientPluginConfigurationTest.class.getClassLoader() .getResource("mock_configs_fetch_response.json")).toURI()))); } + + // mock configs fetch api response + private static String mockLoginResponseJson() { + Map<String, Object> result = new HashMap<>(); + Map<String, Object> data = new HashMap<>(); + data.put("token", "token"); + result.put("data", data); + result.put("code", CommonErrorCode.SUCCESSFUL); + return GsonUtils.getInstance().toJson(result); + } }
shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/config/HttpConfig.java+40 −0 modified@@ -30,6 +30,46 @@ public class HttpConfig { private Integer connectionTimeout; + private String username; + + private String password; + + /** + * get username. + * + * @return username + */ + public String getUsername() { + return username; + } + + /** + * set username. + * + * @param username username + */ + public void setUsername(final String username) { + this.username = username; + } + + /** + * get password. + * + * @return password + */ + public String getPassword() { + return password; + } + + /** + * set password. + * + * @param password password + */ + public void setPassword(final String password) { + this.password = password; + } + /** * get url. *
shenyu-sync-data-center/shenyu-sync-data-http/src/main/java/org/apache/shenyu/sync/data/http/HttpSyncDataService.java+67 −14 modified@@ -25,10 +25,14 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.shenyu.common.concurrent.ShenyuThreadFactory; +import org.apache.shenyu.common.constant.Constants; import org.apache.shenyu.common.constant.HttpConstants; import org.apache.shenyu.common.dto.ConfigData; import org.apache.shenyu.common.enums.ConfigGroupEnum; +import org.apache.shenyu.common.exception.CommonErrorCode; import org.apache.shenyu.common.exception.ShenyuException; +import org.apache.shenyu.common.utils.FreshBeanHolder; +import org.apache.shenyu.common.utils.GsonUtils; import org.apache.shenyu.common.utils.ThreadUtils; import org.apache.shenyu.sync.data.api.AuthDataSubscriber; import org.apache.shenyu.sync.data.api.MetaDataSubscriber; @@ -40,6 +44,7 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.util.LinkedMultiValueMap; @@ -51,6 +56,10 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Map; +import java.util.HashMap; +import java.util.Optional; +import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -67,19 +76,9 @@ public class HttpSyncDataService implements SyncDataService, AutoCloseable { */ private static final Logger LOG = LoggerFactory.getLogger(HttpSyncDataService.class); - private static final AtomicBoolean RUNNING = new AtomicBoolean(false); - private static final Gson GSON = new Gson(); - /** - * shenyu admin path configs fetch. - */ - private static final String SHENYU_ADMIN_PATH_CONFIGS_FETCH = "/configs/fetch"; - - /** - * shenyu admin path configs listener. - */ - private static final String SHENYU_ADMIN_PATH_CONFIGS_LISTENER = "/configs/listener"; + private static final AtomicBoolean RUNNING = new AtomicBoolean(false); /** * default: 10s. @@ -93,15 +92,21 @@ public class HttpSyncDataService implements SyncDataService, AutoCloseable { private ExecutorService executor; + private final HttpConfig httpConfig; + private final List<String> serverList; private final DataRefreshFactory factory; + private final FreshBeanHolder<String, Optional<Object>> accessToken; + public HttpSyncDataService(final HttpConfig httpConfig, final PluginDataSubscriber pluginDataSubscriber, final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) { + this.httpConfig = httpConfig; this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers); this.serverList = Lists.newArrayList(Splitter.on(",").split(httpConfig.getUrl())); this.httpClient = createRestTemplate(); + this.accessToken = new FreshBeanHolder<>(this::doLogin); this.start(); } @@ -149,11 +154,18 @@ private void doFetchGroupConfig(final String server, final ConfigGroupEnum... gr for (ConfigGroupEnum groupKey : groups) { params.append("groupKeys").append("=").append(groupKey.name()).append("&"); } - String url = server + SHENYU_ADMIN_PATH_CONFIGS_FETCH + "?" + StringUtils.removeEnd(params.toString(), "&"); + String url = server + Constants.SHENYU_ADMIN_PATH_CONFIGS_FETCH + "?" + StringUtils.removeEnd(params.toString(), "&"); LOG.info("request configs: [{}]", url); String json; try { - json = this.httpClient.getForObject(url, String.class); + Optional<Object> token = accessToken.apply(server); + if (!token.isPresent()) { + throw new ShenyuException("get token from server : [" + server + " ] error"); + } + HttpHeaders headers = new HttpHeaders(); + headers.set(Constants.X_ACCESS_TOKEN, String.valueOf(token.get())); + HttpEntity<String> httpEntity = new HttpEntity<>(headers); + json = this.httpClient.exchange(url, HttpMethod.GET, httpEntity, String.class).getBody(); } catch (RestClientException e) { String message = String.format("fetch config fail from server[%s], %s", url, e.getMessage()); LOG.warn(message); @@ -184,6 +196,10 @@ private boolean updateCacheWithJson(final String json) { } private void doLongPolling(final String server) { + Optional<Object> token = accessToken.apply(server); + if (!token.isPresent()) { + throw new ShenyuException("get token from server : [" + server + " ] error"); + } MultiValueMap<String, String> params = new LinkedMultiValueMap<>(8); for (ConfigGroupEnum group : ConfigGroupEnum.values()) { ConfigData<?> cacheConfig = factory.cacheConfigData(group); @@ -194,8 +210,9 @@ private void doLongPolling(final String server) { } HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.set(Constants.X_ACCESS_TOKEN, String.valueOf(token.get())); HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers); - String listenerUrl = server + SHENYU_ADMIN_PATH_CONFIGS_LISTENER; + String listenerUrl = server + Constants.SHENYU_ADMIN_PATH_CONFIGS_LISTENER; LOG.debug("request listener configs: [{}]", listenerUrl); JsonArray groupJson; @@ -228,6 +245,40 @@ public void close() { } } + private Optional<Object> doLogin(final String server) { + Map<String, Object> loginMap = new HashMap<>(2); + loginMap.put(Constants.LOGIN_NAME, httpConfig.getUsername()); + loginMap.put(Constants.PASS_WORD, httpConfig.getPassword()); + String param = toQuery(loginMap); + String url = String.join("?", server + Constants.LOGIN_PATH, param); + String result = this.httpClient.getForObject(url, String.class); + Map<String, Object> resultMap = GsonUtils.getInstance().convertToMap(result); + if (!String.valueOf(CommonErrorCode.SUCCESSFUL).equals(String.valueOf(resultMap.get(Constants.ADMIN_RESULT_CODE)))) { + return Optional.empty(); + } + String tokenJson = GsonUtils.getInstance().toJson(resultMap.get(Constants.ADMIN_RESULT_DATA)); + LOG.info("login success: {} ", tokenJson); + Map<String, Object> tokenMap = GsonUtils.getInstance().convertToMap(tokenJson); + return Optional.ofNullable(tokenMap.get(Constants.ADMIN_RESULT_TOKEN)); + } + + private String toQuery(final Object o) { + Map<String, Object> map = null; + if (o instanceof Map) { + map = (Map) o; + } else { + map = GsonUtils.getInstance().convertToMap(GsonUtils.getInstance().toJson(o)); + } + String[] list = new String[((Map) map).size()]; + int i = 0; + + Map.Entry e; + for (Iterator var = ((Map) map).entrySet().iterator(); var.hasNext(); list[i++] = (String) e.getKey() + "=" + e.getValue().toString()) { + e = (Map.Entry) var.next(); + } + return StringUtils.join(list, '&'); + } + class HttpLongPollingTask implements Runnable { private final String server; @@ -242,6 +293,8 @@ public void run() { int retryTimes = 3; for (int time = 1; time <= retryTimes; time++) { try { + //refresh the admin token + accessToken.doFresh(server); doLongPolling(server); } catch (Exception e) { // print warnning LOG.
shenyu-sync-data-center/shenyu-sync-data-http/src/test/java/org/apache/shenyu/sync/data/http/HttpSyncDataServiceTest.java+21 −2 modified@@ -22,6 +22,7 @@ import org.apache.shenyu.common.dto.ConfigData; import org.apache.shenyu.common.dto.PluginData; import org.apache.shenyu.common.enums.ConfigGroupEnum; +import org.apache.shenyu.common.exception.CommonErrorCode; import org.apache.shenyu.common.utils.GsonUtils; import org.apache.shenyu.sync.data.api.AuthDataSubscriber; import org.apache.shenyu.sync.data.api.MetaDataSubscriber; @@ -40,9 +41,9 @@ import wiremock.org.apache.http.entity.ContentType; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; +import java.util.Map; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -76,6 +77,12 @@ public final class HttpSyncDataServiceTest { @Before public void before() { + wireMockRule.stubFor(get(urlPathEqualTo("/platform/login")) + .willReturn(aResponse() + .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) + .withBody(this.mockLoginResponseJson()) + .withStatus(200)) + ); wireMockRule.stubFor(get(urlPathEqualTo("/configs/fetch")) .willReturn(aResponse() .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()) @@ -95,6 +102,8 @@ public void before() { httpConfig.setConnectionTimeout(3000); // set delay time httpConfig.setDelayTime(3); + httpConfig.setPassword("123456"); + httpConfig.setUsername("admin"); this.pluginDataSubscriber = mock(PluginDataSubscriber.class); this.metaDataSubscriber = mock(MetaDataSubscriber.class); this.authDataSubscriber = mock(AuthDataSubscriber.class); @@ -157,4 +166,14 @@ private String mockConfigsFetchResponseJson() { response.put("code", 200); return GsonUtils.getInstance().toJson(response); } + + // mock configs fetch api response + private String mockLoginResponseJson() { + Map<String, Object> result = new HashMap<>(); + Map<String, Object> data = new HashMap<>(); + data.put("token", "token"); + result.put("data", data); + result.put("code", CommonErrorCode.SUCCESSFUL); + return GsonUtils.getInstance().toJson(result); + } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-7rjp-fgwj-47rwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23945ghsaADVISORY
- www.openwall.com/lists/oss-security/2022/01/25/6ghsamailing-listx_refsource_MLISTWEB
- www.openwall.com/lists/oss-security/2022/01/26/3ghsamailing-listx_refsource_MLISTWEB
- github.com/apache/shenyu/commit/9a02215013037e1cc8cd41f216164628a9e9e28fghsaWEB
- github.com/apache/shenyu/pull/2723ghsaWEB
- lists.apache.org/thread/q2gg6ny6lpkph7nkrvjzqdvqpm805v8sghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.