Просмотр исходного кода

feat(系统配置): base-path值正确性校验 (#286)

* feat(系统配置): base-path值正确性校验

* feat(系统配置): base-path值正确性校验

* feat(系统配置): base-path值正确性校验
bestfeng1020 2 лет назад
Родитель
Сommit
4c370c1924

+ 2 - 0
jetlinks-components/common-component/src/main/java/org/jetlinks/community/config/entity/ConfigEntity.java

@@ -6,6 +6,7 @@ import lombok.Setter;
 import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
 import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
 import org.hswebframework.web.api.crud.entity.GenericEntity;
+import org.hswebframework.web.crud.annotation.EnableEntityEvent;
 import org.hswebframework.web.utils.DigestUtils;
 import org.springframework.util.StringUtils;
 
@@ -20,6 +21,7 @@ import java.util.Map;
 })
 @Getter
 @Setter
+@EnableEntityEvent
 public class ConfigEntity extends GenericEntity<String> {
 
     @Column(length = 64, nullable = false, updatable = false)

+ 85 - 0
jetlinks-components/common-component/src/main/java/org/jetlinks/community/config/verification/ConfigVerificationService.java

@@ -0,0 +1,85 @@
+package org.jetlinks.community.config.verification;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.hswebframework.web.crud.events.EntitySavedEvent;
+import org.hswebframework.web.exception.BusinessException;
+import org.jetlinks.community.config.entity.ConfigEntity;
+import org.jetlinks.reactor.ql.utils.CastUtils;
+import org.springframework.context.event.EventListener;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * @author bestfeng
+ */
+@RestController
+public class ConfigVerificationService {
+
+
+    private final WebClient webClient;
+
+    private static final String PATH_VERIFICATION_URI = "/system/config/base-path/verification";
+
+    public ConfigVerificationService() {
+        this.webClient = WebClient
+            .builder()
+            .build();
+    }
+
+    @GetMapping(value = PATH_VERIFICATION_URI)
+    @Operation(description = "basePath配置验证接口")
+    public Mono<Void> basePathValidate(ServerWebExchange response) {
+        response.getResponse().getHeaders().set("auth", PATH_VERIFICATION_URI);
+        return Mono.empty();
+    }
+
+
+    @EventListener
+    public void handleConfigSavedEvent(EntitySavedEvent<ConfigEntity> event){
+        //base-path校验
+        event.async(
+            Flux.fromIterable(event.getEntity())
+                .filter(config -> Objects.equals(config.getScope(), "paths"))
+                .flatMap(config-> doBasePathValidate(config.getProperties().get("base-path")))
+        );
+    }
+
+
+    public Mono<Void> doBasePathValidate(Object basePath) {
+        if (basePath == null) {
+            return Mono.empty();
+        }
+        return webClient
+            .get()
+            .uri(CastUtils.castString(basePath).concat(PATH_VERIFICATION_URI))
+            .exchangeToMono(cr -> {
+                if (cr.statusCode().is2xxSuccessful()
+                    && Objects.equals(cr.headers().asHttpHeaders().getFirst("auth"), PATH_VERIFICATION_URI)) {
+                    return Mono.empty();
+                }
+                return Mono.defer(() -> Mono.error(new BusinessException("error.base_path_error")));
+            })
+            .timeout(Duration.ofSeconds(3), Mono.error(TimeoutException::new))
+            .onErrorResume(err -> {
+                while (err != null) {
+                    if (err instanceof TimeoutException) {
+                        return Mono.error(() -> new BusinessException("error.base_path_validate_request_timeout"));
+                    } else if (err instanceof UnknownHostException) {
+                        return Mono.error(() -> new BusinessException("error.base_path_DNS_resolution_failed"));
+                    }
+                    err = err.getCause();
+                }
+                return Mono.error(() -> new BusinessException("error.base_path_error"));
+            })
+            .then();
+    }
+}

+ 1 - 0
jetlinks-components/common-component/src/main/java/org/jetlinks/community/config/web/SystemConfigManagerController.java

@@ -98,6 +98,7 @@ public class SystemConfigManagerController {
     @Operation(description = "批量保存配置")
     @Transactional
     public Mono<Void> saveConfig(@RequestBody Flux<Scope> scope) {
+
         return scope
             .flatMap(scopeConfig -> configManager.setProperties(scopeConfig.getScope(), scopeConfig.getProperties()))
             .then();

+ 7 - 1
jetlinks-components/common-component/src/main/resources/i18n/common-component/messages_en.properties

@@ -1,4 +1,10 @@
 message.device_message_handing=Message sent to device, processing...
 
 error.duplicate_key_detail=Duplicate Data:{0}
-error.data.referenced=The data has been used elsewhere
+error.data.referenced=The data has been used elsewhere
+error.base_path_error=base-path error. \
+  format\uFF1A{http/https}: //{IP address of the server where the front-end is located}:{Front end exposed service port}/api
+error.base_path_DNS_resolution_failed=base-path DNS resolution failed\u3002\
+  format\uFF1A{http/https}: //{IP address of the server where the front-end is located}:{Front end exposed service port}/api
+error.base_path_validate_request_timeout=base-path validate request timeout\u3002\
+  format\uFF1A{http/https}: //{IP address of the server where the front-end is located}:{Front end exposed service port}/api

+ 6 - 3
jetlinks-components/common-component/src/main/resources/i18n/common-component/messages_zh.properties

@@ -1,3 +1,6 @@
-message.device_message_handing=消息已发往设备,处理中...
-error.data.referenced=数据已经被其他地方使用
-error.duplicate_key_detail=重复的数据:{0}
+message.device_message_handing=\u6D88\u606F\u5DF2\u53D1\u5F80\u8BBE\u5907,\u5904\u7406\u4E2D...
+error.data.referenced=\u6570\u636E\u5DF2\u7ECF\u88AB\u5176\u4ED6\u5730\u65B9\u4F7F\u7528
+error.duplicate_key_detail=\u91CD\u590D\u7684\u6570\u636E:{0}
+error.base_path_error=base-path\u9519\u8BEF\u3002 \u6B63\u786E\u683C\u5F0F\uFF1A{http/https}: //{\u524D\u7AEF\u6240\u5728\u670D\u52A1\u5668IP\u5730\u5740}:{\u524D\u7AEF\u66B4\u9732\u7684\u670D\u52A1\u7AEF\u53E3}/api
+error.base_path_DNS_resolution_failed=base-path DNS\u89E3\u6790\u5931\u8D25\u3002\u6B63\u786E\u683C\u5F0F\uFF1A{http/https}: //{\u524D\u7AEF\u6240\u5728\u670D\u52A1\u5668IP\u5730\u5740}:{\u524D\u7AEF\u66B4\u9732\u7684\u670D\u52A1\u7AEF\u53E3}/api
+error.base_path_validate_request_timeout=base-path \u8BF7\u6C42\u9A8C\u8BC1\u8D85\u65F6\u3002\u6B63\u786E\u683C\u5F0F\uFF1A{http/https}: //{\u524D\u7AEF\u6240\u5728\u670D\u52A1\u5668IP\u5730\u5740}:{\u524D\u7AEF\u66B4\u9732\u7684\u670D\u52A1\u7AEF\u53E3}/api