feat:add unauth api

This commit is contained in:
zzs 2024-10-31 09:18:18 +08:00
parent 37a1ed6310
commit 64092f405f
9 changed files with 306 additions and 7 deletions

View File

@ -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>

View File

@ -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);
}
}
});
}));
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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'

View File

@ -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;
}
}

View File

@ -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 {