From 64092f405f398a0be6a7125622ea84cebe358d78 Mon Sep 17 00:00:00 2001
From: zzs <hi@lnbiuc.copm>
Date: Thu, 31 Oct 2024 09:18:18 +0800
Subject: [PATCH] feat:add unauth api

---
 ensign-gateway/pom.xml                        |  6 ++
 .../conf/CorsResponseHeaderFilter.java        | 65 ++++++++++++++++
 .../ensigngateway/conf/GlobalCorsConfig.java  | 36 +++++++++
 .../conf/GlobalResponseLogFilter.java         | 74 ++++++++++++++++++
 .../ensigngateway/conf/ProxyFilter.java       | 10 +--
 .../ensigngateway/conf/RequestLogFilter.java  | 76 +++++++++++++++++++
 .../src/main/resources/application.yml        | 12 ++-
 .../crm/controller/crm/ProxyController.java   | 31 ++++++++
 .../crm/module/crm/service/ProxyService.java  |  3 +
 9 files changed, 306 insertions(+), 7 deletions(-)
 create mode 100644 ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/CorsResponseHeaderFilter.java
 create mode 100644 ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalCorsConfig.java
 create mode 100644 ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalResponseLogFilter.java
 create mode 100644 ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/RequestLogFilter.java

diff --git a/ensign-gateway/pom.xml b/ensign-gateway/pom.xml
index b2a82ef..2178ad5 100644
--- a/ensign-gateway/pom.xml
+++ b/ensign-gateway/pom.xml
@@ -48,6 +48,11 @@
             <artifactId>fastjson2</artifactId>
             <version>2.0.52</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
     </dependencies>
     <dependencyManagement>
         <dependencies>
@@ -59,6 +64,7 @@
                 <scope>import</scope>
             </dependency>
         </dependencies>
+
     </dependencyManagement>
 
     <build>
diff --git a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/CorsResponseHeaderFilter.java b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/CorsResponseHeaderFilter.java
new file mode 100644
index 0000000..d8d16a1
--- /dev/null
+++ b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/CorsResponseHeaderFilter.java
@@ -0,0 +1,65 @@
+package com.ensign.ensigngateway.conf;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: TODO
+ * @Date: 2024/10/30 9:30
+ * @Created: by ZZSLL
+ */
+
+@Component
+public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
+
+    private static final Logger logger = LoggerFactory.getLogger(CorsResponseHeaderFilter.class);
+
+    private static final String ANY = "*";
+
+    @Override
+    public int getOrder() {
+        // 指定此过滤器位于NettyWriteResponseFilter之后
+        // 即待处理完响应体后接着处理响应头
+        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
+    }
+
+    @Override
+    @SuppressWarnings("serial")
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
+            exchange.getResponse().getHeaders().entrySet().stream()
+                    .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
+                    .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
+                            || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
+                            || kv.getKey().equals(HttpHeaders.VARY)))
+                    .forEach(kv ->
+                    {
+                        // Vary只需要去重即可
+                        if(kv.getKey().equals(HttpHeaders.VARY))
+                            kv.setValue(kv.getValue().stream().distinct().collect(Collectors.toList()));
+                        else{
+                            List<String> value = new ArrayList<>();
+                            if(kv.getValue().contains(ANY)){  //如果包含*,则取*
+                                value.add(ANY);
+                                kv.setValue(value);
+                            }else{
+                                value.add(kv.getValue().get(0)); // 否则默认取第一个
+                                kv.setValue(value);
+                            }
+                        }
+                    });
+        }));
+    }
+}
diff --git a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalCorsConfig.java b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalCorsConfig.java
new file mode 100644
index 0000000..ce19e0e
--- /dev/null
+++ b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalCorsConfig.java
@@ -0,0 +1,36 @@
+package com.ensign.ensigngateway.conf;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+/**
+ * @Description: TODO
+ * @Date: 2024/10/30 9:21
+ * @Created: by ZZSLL
+ */
+
+
+@Configuration
+public class GlobalCorsConfig {
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        CorsConfiguration config = new CorsConfiguration();
+        // 这里仅为了说明问题,配置为放行所有域名,生产环境请对此进行修改
+        config.addAllowedOriginPattern("*");
+        // 放行的请求头
+        config.addAllowedHeader("*");
+        // 放行的请求方式,主要有:GET, POST, PUT, DELETE, OPTIONS
+        config.addAllowedMethod("*");
+        // 暴露头部信息
+        config.addExposedHeader("*");
+        // 是否发送cookie
+        config.setAllowCredentials(true);
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(source);
+    }
+}
diff --git a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalResponseLogFilter.java b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalResponseLogFilter.java
new file mode 100644
index 0000000..2b25e69
--- /dev/null
+++ b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/GlobalResponseLogFilter.java
@@ -0,0 +1,74 @@
+package com.ensign.ensigngateway.conf;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @Description: TODO
+ * @Date: 2024/10/30 9:34
+ * @Created: by ZZSLL
+ */
+
+@Slf4j
+@Configuration
+public class GlobalResponseLogFilter implements GlobalFilter, Ordered {
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        // 打印请求路径
+        String path = request.getPath().pathWithinApplication().value();
+        return chain.filter(exchange).then(
+                Mono.fromRunnable(() -> {
+                    MultiValueMap<String, String> queryParams = request.getQueryParams();
+                    String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();
+
+                    // 构建成一条长日志
+                    StringBuilder responseLog = new StringBuilder(200);
+                    // 日志参数
+                    List<Object> responseArgs = new ArrayList<>();
+                    responseLog.append("\n\n================ Gateway Response Start  ================\n");
+                    ServerHttpResponse response = exchange.getResponse();
+                    // 状态码个path占位符: 200 get: /xxx/xxx/xxx?a=b
+                    responseLog.append("<=== {} {}: {}\n");
+                    // 参数
+                    String requestMethod = request.getMethod().toString();
+                    responseArgs.add(Objects.requireNonNull(response.getStatusCode()).value());
+                    responseArgs.add(requestMethod);
+                    responseArgs.add(requestUrl);
+
+                    // 打印请求头
+                    HttpHeaders headers = response.getHeaders();
+                    headers.forEach((headerName, headerValue) -> {
+                        responseLog.append("===Headers===  {}: {}\n");
+                        responseArgs.add(headerName);
+                        responseArgs.add(String.join(StringUtils.join(headerValue.toArray())));
+                    });
+
+                    responseLog.append("================  Gateway Response End  =================\n");
+                    // 打印执行时间
+                    log.info(responseLog.toString(), responseArgs.toArray());
+                })
+        );
+    }
+
+    @Override
+    public int getOrder() {
+        return Ordered.HIGHEST_PRECEDENCE;
+    }
+}
diff --git a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/ProxyFilter.java b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/ProxyFilter.java
index 77b54db..6038fdb 100644
--- a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/ProxyFilter.java
+++ b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/ProxyFilter.java
@@ -7,14 +7,12 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
 import org.springframework.http.MediaType;
 import org.springframework.stereotype.Component;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
-import org.springframework.http.HttpStatusCode;
-
-import java.util.UUID;
 
 /**
  * @Description: TODO
@@ -36,10 +34,8 @@ public class ProxyFilter implements GlobalFilter {
         String path = exchange.getRequest().getURI().getPath();
         log.info("path {}", path);
 
+        String userId = exchange.getRequest().getHeaders().getFirst("userid");
         if (path.startsWith("/ierp/kapi/")) {
-            String userId = exchange.getRequest().getHeaders().getFirst("userid");
-
-
             log.info("userId: {}", userId);
 
             return webClient.get()
@@ -48,6 +44,7 @@ public class ProxyFilter implements GlobalFilter {
                     .accept(MediaType.APPLICATION_JSON)
                     .retrieve()
                     .onStatus(HttpStatusCode::is4xxClientError, response -> {
+                        log.error(response.statusCode().toString());
                         return Mono.error(new RuntimeException("Unauthorized"));
                     })
                     .bodyToMono(String.class)
@@ -59,6 +56,7 @@ public class ProxyFilter implements GlobalFilter {
                             String token = respJson.getString("data");
                             exchange.getRequest().mutate()
                                     .header("access_token", token)
+                                    .header("userid", userId)
                                     .build();
                             log.info("token: {}", token);
                         }
diff --git a/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/RequestLogFilter.java b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/RequestLogFilter.java
new file mode 100644
index 0000000..07496e5
--- /dev/null
+++ b/ensign-gateway/src/main/java/com/ensign/ensigngateway/conf/RequestLogFilter.java
@@ -0,0 +1,76 @@
+package com.ensign.ensigngateway.conf;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+/**
+ * @Description: TODO
+ * @Date: 2024/10/30 9:32
+ * @Created: by ZZSLL
+ */
+
+@Slf4j
+@Component
+public class RequestLogFilter implements GlobalFilter, Ordered {
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        // 打印请求路径
+        String path = request.getPath().pathWithinApplication().value();
+        // 打印请求url
+        String requestUrl = this.getOriginalRequestUrl(exchange);
+
+        // **构建成一条长 日志,避免并发下日志错乱**
+        StringBuilder reqLog = new StringBuilder(200);
+        // 日志参数
+        List<Object> reqArgs = new ArrayList<>();
+        reqLog.append("\n\n================ Gateway Request Start  ================\n");
+        // 打印路由添加占位符
+        reqLog.append("===> {}: {}\n");
+        // 参数
+        String requestMethod = request.getMethod().name();
+        reqArgs.add(requestMethod);
+        reqArgs.add(requestUrl);
+
+        // 打印请求头
+        HttpHeaders headers = request.getHeaders();
+        headers.forEach((headerName, headerValue) -> {
+            reqLog.append("===Headers===  {}: {}\n");
+            reqArgs.add(headerName);
+        });
+
+        reqLog.append("================  Gateway Request End  =================\n");
+        // 打印执行时间
+        log.info(reqLog.toString(), reqArgs.toArray());
+        return chain.filter(exchange);
+    }
+
+    private String getOriginalRequestUrl(ServerWebExchange exchange) {
+        ServerHttpRequest request = exchange.getRequest();
+        LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
+        URI requestUri = uris.stream().findFirst().orElse(request.getURI());
+        MultiValueMap<String, String> queryParams = request.getQueryParams();
+        return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
+    }
+
+    @Override
+    public int getOrder() {
+        return Ordered.LOWEST_PRECEDENCE;
+    }
+}
\ No newline at end of file
diff --git a/ensign-gateway/src/main/resources/application.yml b/ensign-gateway/src/main/resources/application.yml
index d15002d..6933520 100644
--- a/ensign-gateway/src/main/resources/application.yml
+++ b/ensign-gateway/src/main/resources/application.yml
@@ -4,6 +4,9 @@ spring:
 
   cloud:
     gateway:
+#      globalcors:
+#        add-to-simple-url-handler-mapping: true
+
       routes:
         - id: proxy_route
           uri: ${kingdee.test-inner-end-point}
@@ -12,6 +15,13 @@ spring:
           filters:
             - RewritePath=/crm-api/proxy/do/(?<segment>.*), /${segment}
 
+        - id: system-app-api
+          uri: http://127.0.0.1:38080
+          predicates:
+            - Path=/admin-api/**
+          filters:
+            - RewritePath=/admin-api/(?<segment>.*), /${segment}
+
   servlet:
     multipart:
       max-file-size: 20MB
@@ -24,4 +34,4 @@ kingdee:
   test-inner-end-point: 'http://10.64.112.152:8022'
   prod-public-end-point: 'http://122.4.221.130:8022'
   prod-inner-end-point: 'http://10.64.111.134:8022'
-  local: 'http://127.0.0.1:48080'
+  local: 'http://127.0.0.1:38080'
diff --git a/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/controller/crm/ProxyController.java b/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/controller/crm/ProxyController.java
index 95e8889..5138b09 100644
--- a/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/controller/crm/ProxyController.java
+++ b/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/controller/crm/ProxyController.java
@@ -55,4 +55,35 @@ public class ProxyController {
     public void proxyRead(@RequestParam String path, HttpServletResponse response) throws AllKingdeeException, IOException {
         proxyService.doGetImage(path, response);
     }
+
+    @RequestMapping(value = "/unauth/do/**", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @Operation(summary = "转发接口")
+    @PermitAll
+    public void proxyUnAuth(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException {
+        String requestURI = request.getRequestURI();
+        String[] unAuthPath = new String[]{
+                // 市场活动详情
+                "/crm-api/proxy/unauth/do/ierp/kapi/v2/yem/yem_receipt/yem_crm_marketactivity/getMarketactivities",
+
+                // 获取机型
+                "/crm-api/proxy/unauth/do/ierp/kapi/v2/yem/yem_crmbasic/yem_crm_region/CRM_yem_crm_region",
+
+                // 活动登记
+                "/crm-api/proxy/unauth/do/ierp/kapi/v2/yem/yem_receipt/api/MarketQRCodeAdd"
+        };
+        if (isAuthorized(requestURI, unAuthPath)) {
+            proxyService.doProxy(request, response);
+        } else {
+            response.sendError(HttpServletResponse.SC_FORBIDDEN);
+        }
+    }
+
+    private boolean isAuthorized(String requestURI, String[] unAuthPath) {
+        for (String path : unAuthPath) {
+            if (requestURI.equals(path)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/service/ProxyService.java b/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/service/ProxyService.java
index d7265a9..ac839c0 100644
--- a/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/service/ProxyService.java
+++ b/ensign-module-crm/ensign-module-crm-biz/src/main/java/com/ensign/crm/module/crm/service/ProxyService.java
@@ -88,6 +88,9 @@ public class ProxyService {
         URI uri = new URI(request.getRequestURI());
         String path = uri.getPath();
         String query = request.getQueryString();
+        if (path.contains("/unauth")) {
+            path = path.replace("/unauth", "");
+        }
         String target = initBasePath() + path.replace("/crm-api/proxy/do", "");
         String accessToken;
         try {