From 4685991a2bb53e7ef93180da0d003143902b9a19 Mon Sep 17 00:00:00 2001 From: weiyachao <13526234727@126.com> Date: Thu, 21 Sep 2023 17:46:39 +0800 Subject: [PATCH] =?UTF-8?q?gitway=20=E6=B7=BB=E5=8A=A0=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iot-gateway/pom.xml | 12 ++- .../config/GlobalCorsConfiguration.java | 45 ++++++++++ .../config/properties/CorsProperties.java | 48 ++++++++++ .../config/properties/XssProperties.java | 31 +++++++ .../filter/GlobalCacheRequestFilter.java | 40 +++++++++ .../handler/GatewayExceptionHandler.java | 47 ++++++++++ .../qiuguo/iot/gateway/util/WebFluxUtils.java | 88 +++++++++++++++++++ 7 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java diff --git a/iot-gateway/pom.xml b/iot-gateway/pom.xml index 9516122..6e514d6 100644 --- a/iot-gateway/pom.xml +++ b/iot-gateway/pom.xml @@ -49,7 +49,17 @@ org.springframework.boot spring-boot-starter-actuator - + + org.projectlombok + lombok + + + com.alibaba + fastjson + 1.2.83 + compile + + diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java new file mode 100644 index 0000000..7810bbd --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java @@ -0,0 +1,45 @@ +package com.qiuguo.iot.gateway.config; + +import com.qiuguo.iot.gateway.config.properties.CorsProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +/** + * 全局跨域配置. + * + */ +@AutoConfiguration +@EnableConfigurationProperties(CorsProperties.class) +public class GlobalCorsConfiguration { + + /** + * 允许跨域调用的过滤器. + * + * @param corsProperties 跨域配置 + */ + @Bean + @ConditionalOnMissingBean(CorsFilter.class) + public CorsFilter corsFilter(CorsProperties corsProperties) { + CorsConfiguration config = new CorsConfiguration(); + // 设置允许跨域访问的域名 + config.setAllowedOriginPatterns(corsProperties.getAllowedOriginPatterns()); + // 设置允许跨域访问的方法 + config.setAllowedMethods(corsProperties.getAllowedMethods()); + // 设置允许跨域访问的请求头 + config.setAllowedHeaders(corsProperties.getAllowedHeaders()); + config.setExposedHeaders(corsProperties.getExposedHeaders()); + // 允许跨越发送cookie + config.setAllowCredentials(corsProperties.getAllowCredentials()); + config.setMaxAge(corsProperties.getMaxAge()); + + // 对接口配置跨域设置 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java new file mode 100644 index 0000000..a0ef19d --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java @@ -0,0 +1,48 @@ +package com.qiuguo.iot.gateway.config.properties; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 跨域配置. + * + * @author yangning + * @since 2022/11/7 14:45 + */ +@Getter +@Setter +@ConfigurationProperties(prefix = "application.cors") +public class CorsProperties { + + /** + * 允许跨域访问的域名. + */ + private List allowedOriginPatterns; + + /** + * 允许跨域访问的方法. + */ + private List allowedMethods; + + /** + * 允许跨域访问的请求头. + */ + private List allowedHeaders; + + /** + * 暴露的响应头. + */ + private List exposedHeaders; + + /** + * 是否允许跨域发送cookie. + */ + private Boolean allowCredentials = true; + + /** + * 跨域访问有效期. + */ + private Long maxAge = 1800L; +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..622adf3 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java @@ -0,0 +1,31 @@ +package com.qiuguo.iot.gateway.config.properties; + +import java.util.ArrayList; +import java.util.List; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +/** + * Xss配置. + * + * @author yangning + * @since 2023/4/7 18:31 + */ +@Data +@Component +@RefreshScope +@ConfigurationProperties(prefix = "security.xss") +public class XssProperties { + + /** + * Xss开关. + */ + private Boolean enabled; + + /** + * 排除路径. + */ + private List excludeUrls = new ArrayList<>(); +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java new file mode 100644 index 0000000..93132e8 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java @@ -0,0 +1,40 @@ +package com.qiuguo.iot.gateway.filter; + +import com.qiuguo.iot.gateway.util.WebFluxUtils; +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.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 全局缓存获取body请求数据(解决流不能重复读取问题). + * + * @author yangning + * @since 2022/12/14 16:07 + */ +@Component +public class GlobalCacheRequestFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 只缓存json类型请求 + // if (!WebFluxUtils.isJsonRequest(exchange)) { + // return chain.filter(exchange); + // } + return ServerWebExchangeUtils.cacheRequestBody(exchange, (serverHttpRequest) -> { + if (serverHttpRequest == exchange.getRequest()) { + return chain.filter(exchange); + } + return chain.filter(exchange.mutate().request(serverHttpRequest).build()); + }); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..9b3840c --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,47 @@ +package com.qiuguo.iot.gateway.handler; + +import com.qiuguo.iot.gateway.util.WebFluxUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.cloud.gateway.support.NotFoundException; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关统一异常处理. + * + */ +@Slf4j +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler { + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + final ServerHttpResponse response = exchange.getResponse(); + + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + + String msg; + + if (ex instanceof NotFoundException) { + msg = "服务未找到"; + } else if (ex instanceof ResponseStatusException) { + ResponseStatusException responseStatusException = (ResponseStatusException) ex; + msg = responseStatusException.getMessage(); + } else { + msg = "内部服务器错误"; + } + + log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return WebFluxUtils.webFluxResponseWriter(response, msg); + } + +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java new file mode 100644 index 0000000..5e5eef9 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java @@ -0,0 +1,88 @@ +package com.qiuguo.iot.gateway.util; + +import com.alibaba.fastjson.JSON; +import java.util.HashMap; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * WebFlux 工具类. + */ +public class WebFluxUtils { + + /** + * 是否是Json请求. + * + * @param exchange HTTP请求 + */ + public static boolean isJsonRequest(ServerWebExchange exchange) { + String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value, int code) { + return webFluxResponseWriter(response, HttpStatus.OK, value, code); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, + int code) { + return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, + Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + HashMap map = new HashMap<>(); + map.put("message", value.toString()); + map.put("status", code); + map.put("code", "error"); + map.put("timestamp", System.currentTimeMillis()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(map).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +}