From c80c93bdc2ff3dc0647c3c4272e0aad7d07ef077 Mon Sep 17 00:00:00 2001
From: fst1996 <2411194573@qq.com>
Date: Sun, 17 Sep 2023 10:16:56 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 47 ++++++
pom.xml | 129 +++++++++++++++++
.../gateway/BaWeiGatewayApplication.java | 20 +++
.../bawei/gateway/config/CaptchaConfig.java | 83 +++++++++++
.../bawei/gateway/config/GatewayConfig.java | 23 +++
.../gateway/config/KaptchaTextCreator.java | 75 ++++++++++
.../config/RouterFunctionConfiguration.java | 31 ++++
.../bawei/gateway/config/SwaggerProvider.java | 79 ++++++++++
.../config/properties/CaptchaProperties.java | 46 ++++++
.../properties/IgnoreWhiteProperties.java | 33 +++++
.../config/properties/XssProperties.java | 48 +++++++
.../com/bawei/gateway/filter/AuthFilter.java | 135 ++++++++++++++++++
.../gateway/filter/BlackListUrlFilter.java | 65 +++++++++
.../gateway/filter/CacheRequestFilter.java | 87 +++++++++++
.../gateway/filter/ValidateCodeFilter.java | 79 ++++++++++
.../com/bawei/gateway/filter/XssFilter.java | 124 ++++++++++++++++
.../handler/GatewayExceptionHandler.java | 56 ++++++++
.../handler/SentinelFallbackHandler.java | 41 ++++++
.../bawei/gateway/handler/SwaggerHandler.java | 56 ++++++++
.../gateway/handler/ValidateCodeHandler.java | 41 ++++++
.../gateway/service/ValidateCodeService.java | 23 +++
.../service/impl/ValidateCodeServiceImpl.java | 119 +++++++++++++++
src/main/resources/banner.txt | 10 ++
src/main/resources/bootstrap.yml | 40 ++++++
src/main/resources/logback.xml | 74 ++++++++++
25 files changed, 1564 insertions(+)
create mode 100644 .gitignore
create mode 100644 pom.xml
create mode 100644 src/main/java/com/bawei/gateway/BaWeiGatewayApplication.java
create mode 100644 src/main/java/com/bawei/gateway/config/CaptchaConfig.java
create mode 100644 src/main/java/com/bawei/gateway/config/GatewayConfig.java
create mode 100644 src/main/java/com/bawei/gateway/config/KaptchaTextCreator.java
create mode 100644 src/main/java/com/bawei/gateway/config/RouterFunctionConfiguration.java
create mode 100644 src/main/java/com/bawei/gateway/config/SwaggerProvider.java
create mode 100644 src/main/java/com/bawei/gateway/config/properties/CaptchaProperties.java
create mode 100644 src/main/java/com/bawei/gateway/config/properties/IgnoreWhiteProperties.java
create mode 100644 src/main/java/com/bawei/gateway/config/properties/XssProperties.java
create mode 100644 src/main/java/com/bawei/gateway/filter/AuthFilter.java
create mode 100644 src/main/java/com/bawei/gateway/filter/BlackListUrlFilter.java
create mode 100644 src/main/java/com/bawei/gateway/filter/CacheRequestFilter.java
create mode 100644 src/main/java/com/bawei/gateway/filter/ValidateCodeFilter.java
create mode 100644 src/main/java/com/bawei/gateway/filter/XssFilter.java
create mode 100644 src/main/java/com/bawei/gateway/handler/GatewayExceptionHandler.java
create mode 100644 src/main/java/com/bawei/gateway/handler/SentinelFallbackHandler.java
create mode 100644 src/main/java/com/bawei/gateway/handler/SwaggerHandler.java
create mode 100644 src/main/java/com/bawei/gateway/handler/ValidateCodeHandler.java
create mode 100644 src/main/java/com/bawei/gateway/service/ValidateCodeService.java
create mode 100644 src/main/java/com/bawei/gateway/service/impl/ValidateCodeServiceImpl.java
create mode 100644 src/main/resources/banner.txt
create mode 100644 src/main/resources/bootstrap.yml
create mode 100644 src/main/resources/logback.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..05d7c76
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,47 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### JRebel ###
+rebel.xml
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+target/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3896536
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,129 @@
+
+
+ com.bawei
+ parent
+ 3.6.0
+
+ 4.0.0
+
+ 3.6.0
+ menghang-gateway
+
+
+ menghang-gateway网关模块
+
+
+
+
+ menghang-public
+ 梦航-public
+ http://192.168.111.130:8081/repository/maven-public/
+
+
+
+
+
+ menghang-releases
+ 梦航-releases
+ http://192.168.111.130:8081/repository/maven-releases/
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-sentinel-gateway
+
+
+
+
+ com.alibaba.csp
+ sentinel-datasource-nacos
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-loadbalancer
+
+
+
+
+ com.github.penggle
+ kaptcha
+
+
+
+
+ com.bawei
+ menghang-common-redis
+ 3.6.0
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.fox.version}
+
+
+ io.springfox
+ springfox-swagger2
+ ${swagger.fox.version}
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.7.2
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/bawei/gateway/BaWeiGatewayApplication.java b/src/main/java/com/bawei/gateway/BaWeiGatewayApplication.java
new file mode 100644
index 0000000..5598141
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/BaWeiGatewayApplication.java
@@ -0,0 +1,20 @@
+package com.bawei.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+/**
+ * 网关启动程序
+ *
+ * @author bawei
+ */
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
+public class BaWeiGatewayApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(BaWeiGatewayApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ ");
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/CaptchaConfig.java b/src/main/java/com/bawei/gateway/config/CaptchaConfig.java
new file mode 100644
index 0000000..4e864fb
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/CaptchaConfig.java
@@ -0,0 +1,83 @@
+package com.bawei.gateway.config;
+
+import java.util.Properties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ *
+ * @author bawei
+ */
+@Configuration
+public class CaptchaConfig
+{
+ @Bean(name = "captchaProducer")
+ public DefaultKaptcha getKaptchaBean()
+ {
+ DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+ Properties properties = new Properties();
+ // 是否有边框 默认为true 我们可以自己设置yes,no
+ properties.setProperty(KAPTCHA_BORDER, "yes");
+ // 验证码文本字符颜色 默认为Color.BLACK
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+ // 验证码图片宽度 默认为200
+ properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+ // 验证码图片高度 默认为50
+ properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+ // 验证码文本字符大小 默认为40
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+ // KAPTCHA_SESSION_KEY
+ properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+ // 验证码文本字符长度 默认为5
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+ // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+ // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+ properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+ Config config = new Config(properties);
+ defaultKaptcha.setConfig(config);
+ return defaultKaptcha;
+ }
+
+ @Bean(name = "captchaProducerMath")
+ public DefaultKaptcha getKaptchaBeanMath()
+ {
+ DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+ Properties properties = new Properties();
+ // 是否有边框 默认为true 我们可以自己设置yes,no
+ properties.setProperty(KAPTCHA_BORDER, "yes");
+ // 边框颜色 默认为Color.BLACK
+ properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+ // 验证码文本字符颜色 默认为Color.BLACK
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+ // 验证码图片宽度 默认为200
+ properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+ // 验证码图片高度 默认为50
+ properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+ // 验证码文本字符大小 默认为40
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+ // KAPTCHA_SESSION_KEY
+ properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+ // 验证码文本生成器
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.bawei.gateway.config.KaptchaTextCreator");
+ // 验证码文本字符间距 默认为2
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+ // 验证码文本字符长度 默认为5
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+ // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+ properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+ // 验证码噪点颜色 默认为Color.BLACK
+ properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+ // 干扰实现类
+ properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+ // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+ properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+ Config config = new Config(properties);
+ defaultKaptcha.setConfig(config);
+ return defaultKaptcha;
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/GatewayConfig.java b/src/main/java/com/bawei/gateway/config/GatewayConfig.java
new file mode 100644
index 0000000..08b7994
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/GatewayConfig.java
@@ -0,0 +1,23 @@
+package com.bawei.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import com.bawei.gateway.handler.SentinelFallbackHandler;
+
+/**
+ * 网关限流配置
+ *
+ * @author bawei
+ */
+@Configuration
+public class GatewayConfig
+{
+ @Bean
+ @Order(Ordered.HIGHEST_PRECEDENCE)
+ public SentinelFallbackHandler sentinelGatewayExceptionHandler()
+ {
+ return new SentinelFallbackHandler();
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/KaptchaTextCreator.java b/src/main/java/com/bawei/gateway/config/KaptchaTextCreator.java
new file mode 100644
index 0000000..6b62190
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/KaptchaTextCreator.java
@@ -0,0 +1,75 @@
+package com.bawei.gateway.config;
+
+import java.util.Random;
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+/**
+ * 验证码文本生成器
+ *
+ * @author bawei
+ */
+public class KaptchaTextCreator extends DefaultTextCreator
+{
+ private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+ @Override
+ public String getText()
+ {
+ Integer result = 0;
+ Random random = new Random();
+ int x = random.nextInt(10);
+ int y = random.nextInt(10);
+ StringBuilder suChinese = new StringBuilder();
+ int randomoperands = random.nextInt(3);
+ if (randomoperands == 0)
+ {
+ result = x * y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("*");
+ suChinese.append(CNUMBERS[y]);
+ }
+ else if (randomoperands == 1)
+ {
+ if ((x != 0) && y % x == 0)
+ {
+ result = y / x;
+ suChinese.append(CNUMBERS[y]);
+ suChinese.append("/");
+ suChinese.append(CNUMBERS[x]);
+ }
+ else
+ {
+ result = x + y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("+");
+ suChinese.append(CNUMBERS[y]);
+ }
+ }
+ else if (randomoperands == 2)
+ {
+ if (x >= y)
+ {
+ result = x - y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("-");
+ suChinese.append(CNUMBERS[y]);
+ }
+ else
+ {
+ result = y - x;
+ suChinese.append(CNUMBERS[y]);
+ suChinese.append("-");
+ suChinese.append(CNUMBERS[x]);
+ }
+ }
+ else
+ {
+ result = x + y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("+");
+ suChinese.append(CNUMBERS[y]);
+ }
+ suChinese.append("=?@" + result);
+ return suChinese.toString();
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/RouterFunctionConfiguration.java b/src/main/java/com/bawei/gateway/config/RouterFunctionConfiguration.java
new file mode 100644
index 0000000..156b8da
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/RouterFunctionConfiguration.java
@@ -0,0 +1,31 @@
+package com.bawei.gateway.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.server.RequestPredicates;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import com.bawei.gateway.handler.ValidateCodeHandler;
+
+/**
+ * 路由配置信息
+ *
+ * @author bawei
+ */
+@Configuration
+public class RouterFunctionConfiguration
+{
+ @Autowired
+ private ValidateCodeHandler validateCodeHandler;
+
+ @SuppressWarnings("rawtypes")
+ @Bean
+ public RouterFunction routerFunction()
+ {
+ return RouterFunctions.route(
+ RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
+ validateCodeHandler);
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/SwaggerProvider.java b/src/main/java/com/bawei/gateway/config/SwaggerProvider.java
new file mode 100644
index 0000000..51476a1
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/SwaggerProvider.java
@@ -0,0 +1,79 @@
+package com.bawei.gateway.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.config.GatewayProperties;
+import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.cloud.gateway.support.NameUtils;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.config.ResourceHandlerRegistry;
+import org.springframework.web.reactive.config.WebFluxConfigurer;
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+/**
+ * 聚合系统接口
+ *
+ * @author bawei
+ */
+@Component
+public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer
+{
+ /**
+ * Swagger2默认的url后缀
+ */
+ public static final String SWAGGER2URL = "/v2/api-docs";
+
+ /**
+ * 网关路由
+ */
+ @Lazy
+ @Autowired
+ private RouteLocator routeLocator;
+
+ @Autowired
+ private GatewayProperties gatewayProperties;
+
+ /**
+ * 聚合其他服务接口
+ *
+ * @return
+ */
+ @Override
+ public List get()
+ {
+ List resourceList = new ArrayList<>();
+ List routes = new ArrayList<>();
+ // 获取网关中配置的route
+ routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
+ gatewayProperties.getRoutes().stream()
+ .filter(routeDefinition -> routes
+ .contains(routeDefinition.getId()))
+ .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
+ .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
+ .filter(predicateDefinition -> !"bawei-auth".equalsIgnoreCase(routeDefinition.getId()))
+ .forEach(predicateDefinition -> resourceList
+ .add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
+ .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)))));
+ return resourceList;
+ }
+
+ private SwaggerResource swaggerResource(String name, String location)
+ {
+ SwaggerResource swaggerResource = new SwaggerResource();
+ swaggerResource.setName(name);
+ swaggerResource.setLocation(location);
+ swaggerResource.setSwaggerVersion("2.0");
+ return swaggerResource;
+ }
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry)
+ {
+ /** swagger-ui 地址 */
+ registry.addResourceHandler("/swagger-ui/**")
+ .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/properties/CaptchaProperties.java b/src/main/java/com/bawei/gateway/config/properties/CaptchaProperties.java
new file mode 100644
index 0000000..b9fbf42
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/properties/CaptchaProperties.java
@@ -0,0 +1,46 @@
+package com.bawei.gateway.config.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 验证码配置
+ *
+ * @author bawei
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "security.captcha")
+public class CaptchaProperties
+{
+ /**
+ * 验证码开关
+ */
+ private Boolean enabled;
+
+ /**
+ * 验证码类型(math 数组计算 char 字符)
+ */
+ private String type;
+
+ public Boolean getEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/properties/IgnoreWhiteProperties.java b/src/main/java/com/bawei/gateway/config/properties/IgnoreWhiteProperties.java
new file mode 100644
index 0000000..73e0691
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/properties/IgnoreWhiteProperties.java
@@ -0,0 +1,33 @@
+package com.bawei.gateway.config.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 放行白名单配置
+ *
+ * @author bawei
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "security.ignore")
+public class IgnoreWhiteProperties
+{
+ /**
+ * 放行白名单配置,网关不校验此处的白名单
+ */
+ private List whites = new ArrayList<>();
+
+ public List getWhites()
+ {
+ return whites;
+ }
+
+ public void setWhites(List whites)
+ {
+ this.whites = whites;
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/config/properties/XssProperties.java b/src/main/java/com/bawei/gateway/config/properties/XssProperties.java
new file mode 100644
index 0000000..fb030ad
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/config/properties/XssProperties.java
@@ -0,0 +1,48 @@
+package com.bawei.gateway.config.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * XSS跨站脚本配置
+ *
+ * @author bawei
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "security.xss")
+public class XssProperties
+{
+ /**
+ * Xss开关
+ */
+ private Boolean enabled;
+
+ /**
+ * 排除路径
+ */
+ private List excludeUrls = new ArrayList<>();
+
+ public Boolean getEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public List getExcludeUrls()
+ {
+ return excludeUrls;
+ }
+
+ public void setExcludeUrls(List excludeUrls)
+ {
+ this.excludeUrls = excludeUrls;
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/filter/AuthFilter.java b/src/main/java/com/bawei/gateway/filter/AuthFilter.java
new file mode 100644
index 0000000..844b96e
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/filter/AuthFilter.java
@@ -0,0 +1,135 @@
+package com.bawei.gateway.filter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.web.server.ServerWebExchange;
+import com.bawei.common.core.constant.CacheConstants;
+import com.bawei.common.core.constant.HttpStatus;
+import com.bawei.common.core.constant.SecurityConstants;
+import com.bawei.common.core.constant.TokenConstants;
+import com.bawei.common.core.utils.JwtUtils;
+import com.bawei.common.core.utils.ServletUtils;
+import com.bawei.common.core.utils.StringUtils;
+import com.bawei.common.redis.service.RedisService;
+import com.bawei.gateway.config.properties.IgnoreWhiteProperties;
+import io.jsonwebtoken.Claims;
+import reactor.core.publisher.Mono;
+
+/**
+ * 网关鉴权
+ *
+ * @author bawei
+ */
+@Component
+public class AuthFilter implements GlobalFilter, Ordered
+{
+ private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
+
+ // 排除过滤的 uri 地址,nacos自行添加
+ @Autowired
+ private IgnoreWhiteProperties ignoreWhite;
+
+ @Autowired
+ private RedisService redisService;
+
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain)
+ {
+ ServerHttpRequest request = exchange.getRequest();
+ ServerHttpRequest.Builder mutate = request.mutate();
+
+ String url = request.getURI().getPath();
+ // 跳过不需要验证的路径
+ if (StringUtils.matches(url, ignoreWhite.getWhites()))
+ {
+ return chain.filter(exchange);
+ }
+ String token = getToken(request);
+ if (StringUtils.isEmpty(token))
+ {
+ return unauthorizedResponse(exchange, "令牌不能为空");
+ }
+ Claims claims = JwtUtils.parseToken(token);
+ if (claims == null)
+ {
+ return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
+ }
+ String userkey = JwtUtils.getUserKey(claims);
+ boolean islogin = redisService.hasKey(getTokenKey(userkey));
+ if (!islogin)
+ {
+ return unauthorizedResponse(exchange, "登录状态已过期");
+ }
+ String userid = JwtUtils.getUserId(claims);
+ String username = JwtUtils.getUserName(claims);
+ if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
+ {
+ return unauthorizedResponse(exchange, "令牌验证失败");
+ }
+
+ // 设置用户信息到请求
+ addHeader(mutate, SecurityConstants.USER_KEY, userkey);
+ addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
+ addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
+ // 内部请求来源参数清除
+ removeHeader(mutate, SecurityConstants.FROM_SOURCE);
+ return chain.filter(exchange.mutate().request(mutate.build()).build());
+ }
+
+ private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
+ {
+ if (value == null)
+ {
+ return;
+ }
+ String valueStr = value.toString();
+ String valueEncode = ServletUtils.urlEncode(valueStr);
+ mutate.header(name, valueEncode);
+ }
+
+ private void removeHeader(ServerHttpRequest.Builder mutate, String name)
+ {
+ mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
+ }
+
+ private Mono unauthorizedResponse(ServerWebExchange exchange, String msg)
+ {
+ log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
+ return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
+ }
+
+ /**
+ * 获取缓存key
+ */
+ private String getTokenKey(String token)
+ {
+ return CacheConstants.LOGIN_TOKEN_KEY + token;
+ }
+
+ /**
+ * 获取请求token
+ */
+ private String getToken(ServerHttpRequest request)
+ {
+ String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
+ // 如果前端设置了令牌前缀,则裁剪掉前缀
+ if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
+ {
+ token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
+ }
+ return token;
+ }
+
+ @Override
+ public int getOrder()
+ {
+ return -200;
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/filter/BlackListUrlFilter.java b/src/main/java/com/bawei/gateway/filter/BlackListUrlFilter.java
new file mode 100644
index 0000000..aba7316
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/filter/BlackListUrlFilter.java
@@ -0,0 +1,65 @@
+package com.bawei.gateway.filter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.stereotype.Component;
+import com.bawei.common.core.utils.ServletUtils;
+
+/**
+ * 黑名单过滤器
+ *
+ * @author bawei
+ */
+@Component
+public class BlackListUrlFilter extends AbstractGatewayFilterFactory
+{
+ @Override
+ public GatewayFilter apply(Config config)
+ {
+ return (exchange, chain) -> {
+
+ String url = exchange.getRequest().getURI().getPath();
+ if (config.matchBlacklist(url))
+ {
+ return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
+ }
+
+ return chain.filter(exchange);
+ };
+ }
+
+ public BlackListUrlFilter()
+ {
+ super(Config.class);
+ }
+
+ public static class Config
+ {
+ private List blacklistUrl;
+
+ private List blacklistUrlPattern = new ArrayList<>();
+
+ public boolean matchBlacklist(String url)
+ {
+ return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
+ }
+
+ public List getBlacklistUrl()
+ {
+ return blacklistUrl;
+ }
+
+ public void setBlacklistUrl(List blacklistUrl)
+ {
+ this.blacklistUrl = blacklistUrl;
+ this.blacklistUrlPattern.clear();
+ this.blacklistUrl.forEach(url -> {
+ this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
+ });
+ }
+ }
+
+}
diff --git a/src/main/java/com/bawei/gateway/filter/CacheRequestFilter.java b/src/main/java/com/bawei/gateway/filter/CacheRequestFilter.java
new file mode 100644
index 0000000..1165f7c
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/filter/CacheRequestFilter.java
@@ -0,0 +1,87 @@
+package com.bawei.gateway.filter;
+
+import java.util.Collections;
+import java.util.List;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * 获取body请求数据(解决流不能重复读取问题)
+ *
+ * @author bawei
+ */
+@Component
+public class CacheRequestFilter extends AbstractGatewayFilterFactory
+{
+ public CacheRequestFilter()
+ {
+ super(Config.class);
+ }
+
+ @Override
+ public String name()
+ {
+ return "CacheRequestFilter";
+ }
+
+ @Override
+ public GatewayFilter apply(Config config)
+ {
+ CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter();
+ Integer order = config.getOrder();
+ if (order == null)
+ {
+ return cacheRequestGatewayFilter;
+ }
+ return new OrderedGatewayFilter(cacheRequestGatewayFilter, order);
+ }
+
+ public static class CacheRequestGatewayFilter implements GatewayFilter
+ {
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain)
+ {
+ // GET DELETE 不过滤
+ HttpMethod method = exchange.getRequest().getMethod();
+ if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE)
+ {
+ return chain.filter(exchange);
+ }
+ return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
+ if (serverHttpRequest == exchange.getRequest())
+ {
+ return chain.filter(exchange);
+ }
+ return chain.filter(exchange.mutate().request(serverHttpRequest).build());
+ });
+ }
+ }
+
+ @Override
+ public List shortcutFieldOrder()
+ {
+ return Collections.singletonList("order");
+ }
+
+ static class Config
+ {
+ private Integer order;
+
+ public Integer getOrder()
+ {
+ return order;
+ }
+
+ public void setOrder(Integer order)
+ {
+ this.order = order;
+ }
+ }
+}
diff --git a/src/main/java/com/bawei/gateway/filter/ValidateCodeFilter.java b/src/main/java/com/bawei/gateway/filter/ValidateCodeFilter.java
new file mode 100644
index 0000000..ec01bda
--- /dev/null
+++ b/src/main/java/com/bawei/gateway/filter/ValidateCodeFilter.java
@@ -0,0 +1,79 @@
+package com.bawei.gateway.filter;
+
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicReference;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.bawei.common.core.utils.ServletUtils;
+import com.bawei.common.core.utils.StringUtils;
+import com.bawei.gateway.config.properties.CaptchaProperties;
+import com.bawei.gateway.service.ValidateCodeService;
+import reactor.core.publisher.Flux;
+
+/**
+ * 验证码过滤器
+ *
+ * @author bawei
+ */
+@Component
+public class ValidateCodeFilter extends AbstractGatewayFilterFactory