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 {