From 806d4b0d8639b3b04c166cc60a69eb6f31d4e6aa Mon Sep 17 00:00:00 2001 From: xuekang <914468783@qq.com> Date: 星期五, 10 五月 2024 20:42:23 +0800 Subject: [PATCH] 初始化 --- ruoyi-gateway/src/main/java/org/dromara/gateway/RuoYiGatewayApplication.java | 23 + ruoyi-gateway/src/main/java/org/dromara/gateway/handler/SentinelFallbackHandler.java | 36 + ruoyi-gateway/src/main/java/org/dromara/gateway/filter/XssFilter.java | 100 ++++ ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/XssProperties.java | 31 + ruoyi-gateway/src/main/java/org/dromara/gateway/filter/ForwardAuthFilter.java | 41 + ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/CustomGatewayProperties.java | 24 + ruoyi-gateway/src/main/java/org/dromara/gateway/config/GatewayConfig.java | 21 + ruoyi-gateway/Dockerfile | 22 + ruoyi-gateway/src/main/resources/logback-plus.xml | 114 +++++ ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/ApiDecryptProperties.java | 28 + ruoyi-gateway/src/main/java/org/dromara/gateway/filter/BlackListUrlFilter.java | 58 ++ ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCorsFilter.java | 58 ++ ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalLogFilter.java | 81 +++ ruoyi-gateway/src/main/java/org/dromara/gateway/handler/GatewayExceptionHandler.java | 46 ++ ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCacheRequestFilter.java | 38 + ruoyi-gateway/pom.xml | 154 +++++++ ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java | 68 +++ ruoyi-gateway/src/main/resources/application.yml | 33 + ruoyi-gateway/src/main/resources/banner.txt | 10 ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/IgnoreWhiteProperties.java | 29 + ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalI18nFilter.java | 41 + ruoyi-gateway/src/main/java/org/dromara/gateway/utils/WebFluxUtils.java | 150 +++++++ 22 files changed, 1,206 insertions(+), 0 deletions(-) diff --git a/ruoyi-gateway/Dockerfile b/ruoyi-gateway/Dockerfile new file mode 100644 index 0000000..ffaec9f --- /dev/null +++ b/ruoyi-gateway/Dockerfile @@ -0,0 +1,22 @@ +#FROM findepi/graalvm:java17-native +FROM openjdk:17.0.2-oraclelinux8 + +MAINTAINER Lion Li + +RUN mkdir -p /ruoyi/gateway/logs \ + /ruoyi/gateway/temp \ + /ruoyi/skywalking/agent + +WORKDIR /ruoyi/gateway + +ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" + +EXPOSE ${SERVER_PORT} + +ADD ./target/ruoyi-gateway.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + #-Dskywalking.agent.service_name=ruoyi-gateway \ + #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ + -jar app.jar \ + -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS} diff --git a/ruoyi-gateway/pom.xml b/ruoyi-gateway/pom.xml new file mode 100644 index 0000000..71e544c --- /dev/null +++ b/ruoyi-gateway/pom.xml @@ -0,0 +1,154 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-cloud-plus</artifactId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-gateway</artifactId> + + <description> + ruoyi-gateway缃戝叧妯″潡 + </description> + + <dependencies> + + <!-- SpringCloud Gateway --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-gateway</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-loadbalancer</artifactId> + </dependency> + + <dependency> + <groupId>com.github.ben-manes.caffeine</groupId> + <artifactId>caffeine</artifactId> + </dependency> + + <!-- SpringCloud Alibaba Nacos --> + <dependency> + <groupId>com.alibaba.cloud</groupId> + <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> + </dependency> + + <!-- SpringCloud Alibaba Nacos Config --> + <dependency> + <groupId>com.alibaba.cloud</groupId> + <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> + </dependency> + + <!-- SpringCloud Alibaba Sentinel Gateway --> + <dependency> + <groupId>com.alibaba.cloud</groupId> + <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> + </dependency> + + <!-- SpringBoot Actuator --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + + <!-- SpringCloud Loadbalancer --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-loadbalancer</artifactId> + </dependency> + + <dependency> + <groupId>cn.hutool</groupId> + <artifactId>hutool-http</artifactId> + </dependency> + + <!-- Sa-Token 鏉冮檺璁よ瘉锛圧eactor鍝嶅簲寮忛泦鎴愶級, 鍦ㄧ嚎鏂囨。锛歨ttp://sa-token.dev33.cn/ --> + <dependency> + <groupId>cn.dev33</groupId> + <artifactId>sa-token-reactor-spring-boot3-starter</artifactId> + <version>${satoken.version}</version> + </dependency> + + <dependency> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-common-sentinel</artifactId> + <exclusions> + <exclusion> + <groupId>com.alibaba.csp</groupId> + <artifactId>sentinel-apache-dubbo3-adapter</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-common-satoken</artifactId> + </dependency> + + <!-- RuoYi Common Redis--> + <dependency> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-common-redis</artifactId> + </dependency> + + <dependency> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-common-tenant</artifactId> + <exclusions> + <exclusion> + <groupId>org.dromara</groupId> + <artifactId>ruoyi-common-mybatis</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- 鑷畾涔夎礋杞藉潎琛�(澶氬洟闃熷紑鍙戜娇鐢�) --> +<!-- <dependency>--> +<!-- <groupId>org.dromara</groupId>--> +<!-- <artifactId>ruoyi-common-loadbalancer</artifactId>--> +<!-- </dependency>--> + + <!-- ELK 鏃ュ織鏀堕泦 --> +<!-- <dependency>--> +<!-- <groupId>org.dromara</groupId>--> +<!-- <artifactId>ruoyi-common-logstash</artifactId>--> +<!-- </dependency>--> + + <!-- skywalking 鏃ュ織鏀堕泦 --> +<!-- <dependency>--> +<!-- <groupId>org.dromara</groupId>--> +<!-- <artifactId>ruoyi-common-skylog</artifactId>--> +<!-- </dependency>--> + + <!-- prometheus 鐩戞帶 --> +<!-- <dependency>--> +<!-- <groupId>org.dromara</groupId>--> +<!-- <artifactId>ruoyi-common-prometheus</artifactId>--> +<!-- </dependency>--> + + </dependencies> + + <build> + <finalName>${project.artifactId}</finalName> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring-boot.version}</version> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/RuoYiGatewayApplication.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/RuoYiGatewayApplication.java new file mode 100644 index 0000000..4f64453 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/RuoYiGatewayApplication.java @@ -0,0 +1,23 @@ +package org.dromara.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +/** + * 缃戝叧鍚姩绋嬪簭 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class RuoYiGatewayApplication { + public static void main(String[] args) { + // 鏍囪 sentinel 绫诲瀷涓� 缃戝叧 + System.setProperty("csp.sentinel.app.type", "1"); + SpringApplication application = new SpringApplication(RuoYiGatewayApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(鈾モ棤鈥库棤)锞夛緸 缃戝叧鍚姩鎴愬姛 醿�(麓凇`醿�)锞� "); + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/config/GatewayConfig.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/GatewayConfig.java new file mode 100644 index 0000000..d3b40dd --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/GatewayConfig.java @@ -0,0 +1,21 @@ +package org.dromara.gateway.config; + +import org.dromara.gateway.handler.SentinelFallbackHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +/** + * 缃戝叧闄愭祦閰嶇疆 + * + * @author ruoyi + */ +@Configuration +public class GatewayConfig { + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SentinelFallbackHandler sentinelGatewayExceptionHandler() { + return new SentinelFallbackHandler(); + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/ApiDecryptProperties.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/ApiDecryptProperties.java new file mode 100644 index 0000000..c038bd4 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/ApiDecryptProperties.java @@ -0,0 +1,28 @@ +package org.dromara.gateway.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +/** + * api瑙e瘑灞炴�ч厤缃被 + * @author wdhcr + */ +@Data +@Component +@RefreshScope +@ConfigurationProperties(prefix = "api-decrypt") +public class ApiDecryptProperties { + + /** + * 鍔犲瘑寮�鍏� + */ + private Boolean enabled; + + /** + * 澶撮儴鏍囪瘑 + */ + private String headerFlag; + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/CustomGatewayProperties.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/CustomGatewayProperties.java new file mode 100644 index 0000000..5996030 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/CustomGatewayProperties.java @@ -0,0 +1,24 @@ +package org.dromara.gateway.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * 鑷畾涔塯ateway鍙傛暟閰嶇疆 + * + * @author Lion Li + */ +@Data +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "spring.cloud.gateway") +public class CustomGatewayProperties { + + /** + * 璇锋眰鏃ュ織 + */ + private Boolean requestLog; + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/IgnoreWhiteProperties.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 0000000..bf27ed6 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/IgnoreWhiteProperties.java @@ -0,0 +1,29 @@ +package org.dromara.gateway.config.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * 鏀捐鐧藉悕鍗曢厤缃� + * + * @author ruoyi + */ +@Data +@NoArgsConstructor +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.ignore") +public class IgnoreWhiteProperties { + /** + * 鏀捐鐧藉悕鍗曢厤缃紝缃戝叧涓嶆牎楠屾澶勭殑鐧藉悕鍗� + */ + private List<String> whites = new ArrayList<>(); + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/XssProperties.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..d75b024 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/config/properties/XssProperties.java @@ -0,0 +1,31 @@ +package org.dromara.gateway.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * XSS璺ㄧ珯鑴氭湰閰嶇疆 + * + * @author ruoyi + */ +@Data +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.xss") +public class XssProperties { + /** + * Xss寮�鍏� + */ + private Boolean enabled; + + /** + * 鎺掗櫎璺緞 + */ + private List<String> excludeUrls = new ArrayList<>(); + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..943b0ba --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java @@ -0,0 +1,68 @@ +package org.dromara.gateway.filter; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.reactor.context.SaReactorSyncHolder; +import cn.dev33.satoken.reactor.filter.SaReactorFilter; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.gateway.config.properties.IgnoreWhiteProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * [Sa-Token 鏉冮檺璁よ瘉] 鎷︽埅鍣� + * + * @author Lion Li + */ +@Configuration +public class AuthFilter { + + /** + * 娉ㄥ唽 Sa-Token 鍏ㄥ眬杩囨护鍣� + */ + @Bean + public SaReactorFilter getSaReactorFilter(IgnoreWhiteProperties ignoreWhite) { + return new SaReactorFilter() + // 鎷︽埅鍦板潃 + .addInclude("/**") + .addExclude("/favicon.ico", "/actuator/**") + // 閴存潈鏂规硶锛氭瘡娆¤闂繘鍏� + .setAuth(obj -> { + // 鐧诲綍鏍¢獙 -- 鎷︽埅鎵�鏈夎矾鐢� + SaRouter.match("/**") + .notMatch(ignoreWhite.getWhites()) + .check(r -> { + // 妫�鏌ユ槸鍚︾櫥褰� 鏄惁鏈塼oken + StpUtil.checkLogin(); + + // 妫�鏌� header 涓� param 閲岀殑 clientid 涓� token 閲岀殑鏄惁涓�鑷� + ServerHttpRequest request = SaReactorSyncHolder.getContext().getRequest(); + String headerCid = request.getHeaders().getFirst(LoginHelper.CLIENT_KEY); + String paramCid = request.getQueryParams().getFirst(LoginHelper.CLIENT_KEY); + String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { + // token 鏃犳晥 + throw NotLoginException.newInstance(StpUtil.getLoginType(), + "-100", "瀹㈡埛绔疘D涓嶵oken涓嶅尮閰�", + StpUtil.getTokenValue()); + } + + // 鏈夋晥鐜囧奖鍝� 鐢ㄤ簬涓存椂娴嬭瘯 + // if (log.isDebugEnabled()) { + // log.debug("鍓╀綑鏈夋晥鏃堕棿: {}", StpUtil.getTokenTimeout()); + // log.debug("涓存椂鏈夋晥鏃堕棿: {}", StpUtil.getTokenActivityTimeout()); + // } + }); + }).setError(e -> { + if (e instanceof NotLoginException) { + return SaResult.error(e.getMessage()).setCode(HttpStatus.UNAUTHORIZED); + } + return SaResult.error("璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�").setCode(HttpStatus.UNAUTHORIZED); + }); + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/BlackListUrlFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/BlackListUrlFilter.java new file mode 100644 index 0000000..177e93c --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/BlackListUrlFilter.java @@ -0,0 +1,58 @@ +package org.dromara.gateway.filter; + +import org.dromara.gateway.utils.WebFluxUtils; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 榛戝悕鍗曡繃婊ゅ櫒 + * + * @author ruoyi + */ +@Component +public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> { + @Override + public GatewayFilter apply(Config config) { + return (exchange, chain) -> { + + String url = exchange.getRequest().getURI().getPath(); + if (config.matchBlacklist(url)) { + return WebFluxUtils.webFluxResponseWriter(exchange.getResponse(), "璇锋眰鍦板潃涓嶅厑璁歌闂�"); + } + + return chain.filter(exchange); + }; + } + + public BlackListUrlFilter() { + super(Config.class); + } + + public static class Config { + private List<String> blacklistUrl; + + private List<Pattern> blacklistUrlPattern = new ArrayList<>(); + + public boolean matchBlacklist(String url) { + return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); + } + + public List<String> getBlacklistUrl() { + return blacklistUrl; + } + + public void setBlacklistUrl(List<String> blacklistUrl) { + this.blacklistUrl = blacklistUrl; + this.blacklistUrlPattern.clear(); + this.blacklistUrl.forEach(url -> { + this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); + }); + } + } + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/ForwardAuthFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/ForwardAuthFilter.java new file mode 100644 index 0000000..fe0348b --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/ForwardAuthFilter.java @@ -0,0 +1,41 @@ +package org.dromara.gateway.filter; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.same.SaSameUtil; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 杞彂璁よ瘉杩囨护鍣�(鍐呴儴鏈嶅姟澶栫綉闅旂) + * + * @author Lion Li + */ +@Component +public class ForwardAuthFilter implements GlobalFilter, Ordered { + @Override + public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 鏈紑鍚厤缃垯鐩存帴璺宠繃 + if (!SaManager.getConfig().getCheckSameToken()) { + return chain.filter(exchange); + } + ServerHttpRequest newRequest = exchange + .getRequest() + .mutate() + // 涓鸿姹傝拷鍔� Same-Token 鍙傛暟 + .header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken()) + .build(); + ServerWebExchange newExchange = exchange.mutate().request(newRequest).build(); + return chain.filter(newExchange); + } + + @Override + public int getOrder() { + return -100; + } +} + diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCacheRequestFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCacheRequestFilter.java new file mode 100644 index 0000000..09e50ff --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCacheRequestFilter.java @@ -0,0 +1,38 @@ +package org.dromara.gateway.filter; + +import org.dromara.gateway.utils.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 Lion Li + */ +@Component +public class GlobalCacheRequestFilter implements GlobalFilter, Ordered { + + @Override + public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 鍙紦瀛榡son绫诲瀷璇锋眰 + 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 + 1; + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCorsFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCorsFilter.java new file mode 100644 index 0000000..9f990aa --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalCorsFilter.java @@ -0,0 +1,58 @@ +package org.dromara.gateway.filter; + +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.cors.reactive.CorsUtils; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + + +/** + * 璺ㄥ煙閰嶇疆 + * + * @author Lion Li + */ +@Component +public class GlobalCorsFilter implements WebFilter, Ordered { + + /** + * 杩欓噷涓烘敮鎸佺殑璇锋眰澶达紝濡傛灉鏈夎嚜瀹氫箟鐨刪eader瀛楁璇疯嚜宸辨坊鍔� + */ + private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Language, Content-Type, Authorization, clientid, credential, X-XSRF-TOKEN, isToken, token, Admin-Token, App-Token"; + private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD"; + private static final String ALLOWED_ORIGIN = "*"; + private static final String ALLOWED_EXPOSE = "*"; + private static final String MAX_AGE = "18000L"; + + @Override + public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + if (CorsUtils.isCorsRequest(request)) { + ServerHttpResponse response = exchange.getResponse(); + HttpHeaders headers = response.getHeaders(); + headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); + headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); + headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN); + headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE); + headers.add("Access-Control-Max-Age", MAX_AGE); + headers.add("Access-Control-Allow-Credentials", "true"); + if (request.getMethod() == HttpMethod.OPTIONS) { + response.setStatusCode(HttpStatus.OK); + return Mono.empty(); + } + } + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalI18nFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalI18nFilter.java new file mode 100644 index 0000000..bad65bf --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalI18nFilter.java @@ -0,0 +1,41 @@ +package org.dromara.gateway.filter; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.i18n.SimpleLocaleContext; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Locale; + +/** + * 鍏ㄥ眬鍥介檯鍖栧鐞� + * + * @author Lion Li + */ +@Slf4j +@Component +public class GlobalI18nFilter implements GlobalFilter, Ordered { + + @Override + public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { + String language = exchange.getRequest().getHeaders().getFirst("content-language"); + Locale locale = Locale.getDefault(); + if (language != null && language.length() > 0) { + String[] split = language.split("_"); + locale = new Locale(split[0], split[1]); + } + LocaleContextHolder.setLocaleContext(new SimpleLocaleContext(locale), true); + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalLogFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalLogFilter.java new file mode 100644 index 0000000..2c77651 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/GlobalLogFilter.java @@ -0,0 +1,81 @@ +package org.dromara.gateway.filter; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.gateway.config.properties.ApiDecryptProperties; +import org.dromara.gateway.config.properties.CustomGatewayProperties; +import org.dromara.gateway.utils.WebFluxUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 鍏ㄥ眬鏃ュ織杩囨护鍣� + * <p> + * 鐢ㄤ簬鎵撳嵃璇锋眰鎵ц鍙傛暟涓庡搷搴旀椂闂寸瓑绛� + * + * @author Lion Li + */ +@Slf4j +@Component +public class GlobalLogFilter implements GlobalFilter, Ordered { + + @Autowired + private CustomGatewayProperties customGatewayProperties; + @Autowired + private ApiDecryptProperties apiDecryptProperties; + + private static final String START_TIME = "startTime"; + + @Override + public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { + if (!customGatewayProperties.getRequestLog()) { + return chain.filter(exchange); + } + ServerHttpRequest request = exchange.getRequest(); + String path = WebFluxUtils.getOriginalRequestUrl(exchange); + String url = request.getMethod().name() + " " + path; + + // 鎵撳嵃璇锋眰鍙傛暟 + if (WebFluxUtils.isJsonRequest(exchange)) { + if (apiDecryptProperties.getEnabled() + && ObjectUtil.isNotNull(request.getHeaders().getFirst(apiDecryptProperties.getHeaderFlag()))) { + log.info("[PLUS]寮�濮嬭姹� => URL[{}],鍙傛暟绫诲瀷[encrypt]", url); + } else { + String jsonParam = WebFluxUtils.resolveBodyFromCacheRequest(exchange); + log.info("[PLUS]寮�濮嬭姹� => URL[{}],鍙傛暟绫诲瀷[json],鍙傛暟:[{}]", url, jsonParam); + } + } else { + MultiValueMap<String, String> parameterMap = request.getQueryParams(); + if (MapUtil.isNotEmpty(parameterMap)) { + String parameters = JsonUtils.toJsonString(parameterMap); + log.info("[PLUS]寮�濮嬭姹� => URL[{}],鍙傛暟绫诲瀷[param],鍙傛暟:[{}]", url, parameters); + } else { + log.info("[PLUS]寮�濮嬭姹� => URL[{}],鏃犲弬鏁�", url); + } + } + + exchange.getAttributes().put(START_TIME, System.currentTimeMillis()); + return chain.filter(exchange).then(Mono.fromRunnable(() -> { + Long startTime = exchange.getAttribute(START_TIME); + if (startTime != null) { + long executeTime = (System.currentTimeMillis() - startTime); + log.info("[PLUS]缁撴潫璇锋眰 => URL[{}],鑰楁椂:[{}]姣", url, executeTime); + } + })); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/XssFilter.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/XssFilter.java new file mode 100644 index 0000000..d501610 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/XssFilter.java @@ -0,0 +1,100 @@ +package org.dromara.gateway.filter; + +import cn.hutool.http.HtmlUtil; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.gateway.config.properties.XssProperties; +import org.dromara.gateway.utils.WebFluxUtils; +import io.netty.buffer.ByteBufAllocator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +/** + * 璺ㄧ珯鑴氭湰杩囨护鍣� + * + * @author ruoyi + */ +@Component +@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") +public class XssFilter implements GlobalFilter, Ordered { + // 璺ㄧ珯鑴氭湰鐨� xss 閰嶇疆锛宯acos鑷娣诲姞 + @Autowired + private XssProperties xss; + + @Override + public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + // GET DELETE 涓嶈繃婊� + HttpMethod method = request.getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + // 闈瀓son绫诲瀷锛屼笉杩囨护 + if (!WebFluxUtils.isJsonRequest(exchange)) { + return chain.filter(exchange); + } + // excludeUrls 涓嶈繃婊� + String url = request.getURI().getPath(); + if (StringUtils.matches(url, xss.getExcludeUrls())) { + return chain.filter(exchange); + } + ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange); + return chain.filter(exchange.mutate().request(httpRequestDecorator).build()); + + } + + private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) { + ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) { + @Override + public Flux<DataBuffer> getBody() { + Flux<DataBuffer> body = super.getBody(); + return body.buffer().map(dataBuffers -> { + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + DataBufferUtils.release(join); + String bodyStr = new String(content, StandardCharsets.UTF_8); + // 闃瞲ss鏀诲嚮杩囨护 + bodyStr = HtmlUtil.cleanHtmlTag(bodyStr); + // 杞垚瀛楄妭 + byte[] bytes = bodyStr.getBytes(); + NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); + DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); + buffer.write(bytes); + return buffer; + }); + } + + @Override + public HttpHeaders getHeaders() { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(super.getHeaders()); + // 鐢变簬淇敼浜嗚姹備綋鐨刡ody锛屽鑷碿ontent-length闀垮害涓嶇‘瀹氾紝鍥犳闇�瑕佸垹闄ゅ師鍏堢殑content-length + httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + return httpHeaders; + } + + }; + return serverHttpRequestDecorator; + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/GatewayExceptionHandler.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..e89538e --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,46 @@ +package org.dromara.gateway.handler; + +import org.dromara.gateway.utils.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; + +/** + * 缃戝叧缁熶竴寮傚父澶勭悊 + * + * @author ruoyi + */ +@Slf4j +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler { + + @Override + public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { + 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) { + msg = responseStatusException.getMessage(); + } else { + msg = "鍐呴儴鏈嶅姟鍣ㄩ敊璇�"; + } + + log.error("[缃戝叧寮傚父澶勭悊]璇锋眰璺緞:{},寮傚父淇℃伅:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return WebFluxUtils.webFluxResponseWriter(response, msg); + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/SentinelFallbackHandler.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/SentinelFallbackHandler.java new file mode 100644 index 0000000..0aec088 --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/handler/SentinelFallbackHandler.java @@ -0,0 +1,36 @@ +package org.dromara.gateway.handler; + +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import org.dromara.gateway.utils.WebFluxUtils; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebExceptionHandler; +import reactor.core.publisher.Mono; + +/** + * 鑷畾涔夐檺娴佸紓甯稿鐞� + * + * @author ruoyi + */ +public class SentinelFallbackHandler implements WebExceptionHandler { + private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) { + return WebFluxUtils.webFluxResponseWriter(exchange.getResponse(), "璇锋眰瓒呰繃鏈�澶ф暟锛岃绋嶅�欏啀璇�"); + } + + @Override + public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { + ex.printStackTrace(); + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + if (!BlockException.isBlockException(ex)) { + return Mono.error(ex); + } + return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); + } + + private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) { + return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); + } +} diff --git a/ruoyi-gateway/src/main/java/org/dromara/gateway/utils/WebFluxUtils.java b/ruoyi-gateway/src/main/java/org/dromara/gateway/utils/WebFluxUtils.java new file mode 100644 index 0000000..278050a --- /dev/null +++ b/ruoyi-gateway/src/main/java/org/dromara/gateway/utils/WebFluxUtils.java @@ -0,0 +1,150 @@ +package org.dromara.gateway.utils; + +import cn.hutool.core.util.ObjectUtil; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.gateway.filter.GlobalCacheRequestFilter; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashSet; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR; + +/** + * WebFlux 宸ュ叿绫� + * + * @author Lion Li + */ +public class WebFluxUtils { + + /** + * 鑾峰彇鍘熻姹傝矾寰� + */ + public static String getOriginalRequestUrl(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + LinkedHashSet<URI> uris = exchange.getAttributeOrDefault(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, new LinkedHashSet<>()); + URI requestUri = uris.stream().findFirst().orElse(request.getURI()); + return UriComponentsBuilder.fromPath(requestUri.getRawPath()).build().toUriString(); + } + + /** + * 鏄惁鏄疛son璇锋眰 + * + * @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); + } + + /** + * 璇诲彇request鍐呯殑body + * + * 娉ㄦ剰涓�涓猺equest鍙兘璇诲彇涓�娆� 璇诲彇涔嬪悗闇�瑕侀噸鏂板寘瑁� + */ + public static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) { + // 鑾峰彇璇锋眰浣� + Flux<DataBuffer> body = serverHttpRequest.getBody(); + AtomicReference<String> bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + try (DataBuffer.ByteBufferIterator iterator = buffer.readableByteBuffers()) { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(iterator.next()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + } + }); + return bodyRef.get(); + } + + /** + * 浠庣紦瀛樹腑璇诲彇request鍐呯殑body + * + * 娉ㄦ剰瑕佹眰缁忚繃 {@link ServerWebExchangeUtils#cacheRequestBody(ServerWebExchange, Function)} 姝ゆ柟娉曞垱寤虹紦瀛� + * 妗嗘灦鍐呭凡缁忎娇鐢� {@link GlobalCacheRequestFilter} 鍏ㄥ眬鍒涘缓浜哹ody缂撳瓨 + * + * @return body + */ + public static String resolveBodyFromCacheRequest(ServerWebExchange exchange) { + Object obj = exchange.getAttributes().get(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR); + if (ObjectUtil.isNull(obj)) { + return null; + } + DataBuffer buffer = (DataBuffer) obj; + try (DataBuffer.ByteBufferIterator iterator = buffer.readableByteBuffers()) { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(iterator.next()); + return charBuffer.toString(); + } + } + + /** + * 璁剧疆webflux妯″瀷鍝嶅簲 + * + * @param response ServerHttpResponse + * @param value 鍝嶅簲鍐呭 + * @return Mono<Void> + */ + public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL); + } + + /** + * 璁剧疆webflux妯″瀷鍝嶅簲 + * + * @param response ServerHttpResponse + * @param code 鍝嶅簲鐘舵�佺爜 + * @param value 鍝嶅簲鍐呭 + * @return Mono<Void> + */ + public static Mono<Void> 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<Void> + */ + public static Mono<Void> 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<Void> + */ + public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + R<?> result = R.fail(code, value.toString()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JsonUtils.toJsonString(result).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/ruoyi-gateway/src/main/resources/application.yml b/ruoyi-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..08bd112 --- /dev/null +++ b/ruoyi-gateway/src/main/resources/application.yml @@ -0,0 +1,33 @@ +# Tomcat +server: + port: 8080 + servlet: + context-path: / + +# Spring +spring: + application: + # 搴旂敤鍚嶇О + name: ruoyi-gateway + profiles: + # 鐜閰嶇疆 + active: @profiles.active@ + +--- # nacos 閰嶇疆 +spring: + cloud: + nacos: + # nacos 鏈嶅姟鍦板潃 + server-addr: @nacos.server@ + discovery: + # 娉ㄥ唽缁� + group: @nacos.discovery.group@ + namespace: ${spring.profiles.active} + config: + # 閰嶇疆缁� + group: @nacos.config.group@ + namespace: ${spring.profiles.active} + config: + import: + - optional:nacos:application-common.yml + - optional:nacos:${spring.application.name}.yml diff --git a/ruoyi-gateway/src/main/resources/banner.txt b/ruoyi-gateway/src/main/resources/banner.txt new file mode 100644 index 0000000..ceced29 --- /dev/null +++ b/ruoyi-gateway/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ + (_) | | + _ __ _ _ ___ _ _ _ ______ __ _ __ _ | |_ ___ __ __ __ _ _ _ +| '__|| | | | / _ \ | | | || ||______| / _` | / _` || __| / _ \\ \ /\ / / / _` || | | | +| | | |_| || (_) || |_| || | | (_| || (_| || |_ | __/ \ V V / | (_| || |_| | +|_| \__,_| \___/ \__, ||_| \__, | \__,_| \__| \___| \_/\_/ \__,_| \__, | + __/ | __/ | __/ | + |___/ |___/ |___/ \ No newline at end of file diff --git a/ruoyi-gateway/src/main/resources/logback-plus.xml b/ruoyi-gateway/src/main/resources/logback-plus.xml new file mode 100644 index 0000000..4d66014 --- /dev/null +++ b/ruoyi-gateway/src/main/resources/logback-plus.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration scan="true" scanPeriod="60 seconds" debug="false"> + <!-- 鏃ュ織瀛樻斁璺緞 --> + <property name="log.path" value="logs/${project.artifactId}"/> + <!-- 鏃ュ織杈撳嚭鏍煎紡 --> + <property name="console.log.pattern" + value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> + <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> + + <!-- 鎺у埗鍙拌緭鍑� --> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>${console.log.pattern}</pattern> + <charset>utf-8</charset> + </encoder> + </appender> + + <!-- 鎺у埗鍙拌緭鍑� --> + <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/console.log</file> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� --> + <fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 鏃ュ織鏈�澶� 1澶� --> + <maxHistory>1</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + <charset>utf-8</charset> + </encoder> + <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> + <!-- 杩囨护鐨勭骇鍒� --> + <level>INFO</level> + </filter> + </appender> + + <!-- 绯荤粺鏃ュ織杈撳嚭 --> + <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/info.log</file> + <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村垱寤烘棩蹇楁枃浠� --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� --> + <fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 杩囨护鐨勭骇鍒� --> + <level>INFO</level> + <!-- 鍖归厤鏃剁殑鎿嶄綔锛氭帴鏀讹紙璁板綍锛� --> + <onMatch>ACCEPT</onMatch> + <!-- 涓嶅尮閰嶆椂鐨勬搷浣滐細鎷掔粷锛堜笉璁板綍锛� --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/error.log</file> + <!-- 寰幆鏀跨瓥锛氬熀浜庢椂闂村垱寤烘棩蹇楁枃浠� --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� --> + <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 杩囨护鐨勭骇鍒� --> + <level>ERROR</level> + <!-- 鍖归厤鏃剁殑鎿嶄綔锛氭帴鏀讹紙璁板綍锛� --> + <onMatch>ACCEPT</onMatch> + <!-- 涓嶅尮閰嶆椂鐨勬搷浣滐細鎷掔粷锛堜笉璁板綍锛� --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <!-- info寮傛杈撳嚭 --> + <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender"> + <!-- 涓嶄涪澶辨棩蹇�.榛樿鐨�,濡傛灉闃熷垪鐨�80%宸叉弧,鍒欎細涓㈠純TRACT銆丏EBUG銆両NFO绾у埆鐨勬棩蹇� --> + <discardingThreshold>0</discardingThreshold> + <!-- 鏇存敼榛樿鐨勯槦鍒楃殑娣卞害,璇ュ�间細褰卞搷鎬ц兘.榛樿鍊间负256 --> + <queueSize>512</queueSize> + <!-- 娣诲姞闄勫姞鐨刟ppender,鏈�澶氬彧鑳芥坊鍔犱竴涓� --> + <appender-ref ref="file_info"/> + </appender> + + <!-- error寮傛杈撳嚭 --> + <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender"> + <!-- 涓嶄涪澶辨棩蹇�.榛樿鐨�,濡傛灉闃熷垪鐨�80%宸叉弧,鍒欎細涓㈠純TRACT銆丏EBUG銆両NFO绾у埆鐨勬棩蹇� --> + <discardingThreshold>0</discardingThreshold> + <!-- 鏇存敼榛樿鐨勯槦鍒楃殑娣卞害,璇ュ�间細褰卞搷鎬ц兘.榛樿鍊间负256 --> + <queueSize>512</queueSize> + <!-- 娣诲姞闄勫姞鐨刟ppender,鏈�澶氬彧鑳芥坊鍔犱竴涓� --> + <appender-ref ref="file_error"/> + </appender> + + <include resource="logback-logstash.xml" /> + + <!-- 寮�鍚� skywalking 鏃ュ織鏀堕泦 --> + <include resource="logback-skylog.xml" /> + + <!--绯荤粺鎿嶄綔鏃ュ織--> + <root level="info"> + <appender-ref ref="console"/> + <appender-ref ref="async_info"/> + <appender-ref ref="async_error"/> + <appender-ref ref="file_console"/> + </root> +</configuration> -- Gitblit v1.9.1