From 48c7cd34bc80ab48c89e0e72514d57279f4b8d2b Mon Sep 17 00:00:00 2001 From: caojianbin <1910336823> Date: Tue, 26 Jul 2022 14:45:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E5=85=B3=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E6=94=B9=E9=80=A008?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/gateway/RuoYiGatewayApplication.java | 29 ----- .../com/ruoyi/gateway/config/CaptchaConfig.java | 83 ------------- .../com/ruoyi/gateway/config/GatewayConfig.java | 23 ---- .../ruoyi/gateway/config/KaptchaTextCreator.java | 75 ----------- .../config/RouterFunctionConfiguration.java | 31 ----- .../com/ruoyi/gateway/config/SwaggerProvider.java | 79 ------------ .../config/properties/CaptchaProperties.java | 46 ------- .../config/properties/IgnoreWhiteProperties.java | 33 ----- .../gateway/config/properties/XssProperties.java | 48 ------- .../ruoyi/gateway/constant/MessagesConstant.java | 77 ------------ .../java/com/ruoyi/gateway/filter/AuthFilter.java | 138 --------------------- .../ruoyi/gateway/filter/BlackListUrlFilter.java | 67 ---------- .../ruoyi/gateway/filter/CacheRequestFilter.java | 87 ------------- .../ruoyi/gateway/filter/ValidateCodeFilter.java | 79 ------------ .../java/com/ruoyi/gateway/filter/XssFilter.java | 124 ------------------ .../gateway/handler/GatewayExceptionHandler.java | 63 ---------- .../gateway/handler/SentinelFallbackHandler.java | 41 ------ .../com/ruoyi/gateway/handler/SwaggerHandler.java | 56 --------- .../ruoyi/gateway/handler/ValidateCodeHandler.java | 41 ------ .../ruoyi/gateway/service/ValidateCodeService.java | 23 ---- .../service/impl/ValidateCodeServiceImpl.java | 118 ------------------ .../com/yisai/gateway/RuoYiGatewayApplication.java | 29 +++++ .../com/yisai/gateway/config/CaptchaConfig.java | 83 +++++++++++++ .../com/yisai/gateway/config/GatewayConfig.java | 23 ++++ .../yisai/gateway/config/KaptchaTextCreator.java | 75 +++++++++++ .../config/RouterFunctionConfiguration.java | 31 +++++ .../com/yisai/gateway/config/SwaggerProvider.java | 79 ++++++++++++ .../config/properties/CaptchaProperties.java | 46 +++++++ .../config/properties/IgnoreWhiteProperties.java | 33 +++++ .../gateway/config/properties/XssProperties.java | 48 +++++++ .../yisai/gateway/constant/MessagesConstant.java | 72 +++++++++++ .../java/com/yisai/gateway/filter/AuthFilter.java | 138 +++++++++++++++++++++ .../yisai/gateway/filter/BlackListUrlFilter.java | 67 ++++++++++ .../yisai/gateway/filter/CacheRequestFilter.java | 87 +++++++++++++ .../yisai/gateway/filter/ValidateCodeFilter.java | 80 ++++++++++++ .../java/com/yisai/gateway/filter/XssFilter.java | 124 ++++++++++++++++++ .../gateway/handler/GatewayExceptionHandler.java | 63 ++++++++++ .../gateway/handler/SentinelFallbackHandler.java | 41 ++++++ .../com/yisai/gateway/handler/SwaggerHandler.java | 56 +++++++++ .../yisai/gateway/handler/ValidateCodeHandler.java | 41 ++++++ .../yisai/gateway/service/ValidateCodeService.java | 23 ++++ .../service/impl/ValidateCodeServiceImpl.java | 118 ++++++++++++++++++ 42 files changed, 1357 insertions(+), 1361 deletions(-) delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/CaptchaProperties.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/XssProperties.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/constant/MessagesConstant.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/XssFilter.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java delete mode 100644 ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/RuoYiGatewayApplication.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/CaptchaConfig.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/GatewayConfig.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/KaptchaTextCreator.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/RouterFunctionConfiguration.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/SwaggerProvider.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/CaptchaProperties.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/IgnoreWhiteProperties.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/XssProperties.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/constant/MessagesConstant.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/filter/AuthFilter.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/filter/BlackListUrlFilter.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/filter/CacheRequestFilter.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/filter/ValidateCodeFilter.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/filter/XssFilter.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/handler/GatewayExceptionHandler.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SentinelFallbackHandler.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SwaggerHandler.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/handler/ValidateCodeHandler.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/service/ValidateCodeService.java create mode 100644 ruoyi-gateway/src/main/java/com/yisai/gateway/service/impl/ValidateCodeServiceImpl.java diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java deleted file mode 100644 index c6f7db6..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.ruoyi.gateway; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; - -/** - * 网关启动程序 - * - * @author ruoyi - */ -@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) -public class RuoYiGatewayApplication -{ - public static void main(String[] args) - { - SpringApplication.run(RuoYiGatewayApplication.class, args); - System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" + - " .-------. ____ __ \n" + - " | _ _ \\ \\ \\ / / \n" + - " | ( ' ) | \\ _. / ' \n" + - " |(_ o _) / _( )_ .' \n" + - " | (_,_).' __ ___(_ o _)' \n" + - " | |\\ \\ | || |(_,_)' \n" + - " | | \\ `' /| `-' / \n" + - " | | \\ / \\ / \n" + - " ''-' `'-' `-..-' "); - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java deleted file mode 100644 index 963425e..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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.ruoyi.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; - } -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java deleted file mode 100644 index 8d0fee4..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ruoyi.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.ruoyi.gateway.handler.SentinelFallbackHandler; - -/** - * 网关限流配置 - * - * @author ruoyi - */ -@Configuration -public class GatewayConfig -{ - @Bean - @Order(Ordered.HIGHEST_PRECEDENCE) - public SentinelFallbackHandler sentinelGatewayExceptionHandler() - { - return new SentinelFallbackHandler(); - } -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java deleted file mode 100644 index 9ec3173..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ruoyi.gateway.config; - -import java.util.Random; -import com.google.code.kaptcha.text.impl.DefaultTextCreator; - -/** - * 验证码文本生成器 - * - * @author ruoyi - */ -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 = (int) Math.round(Math.random() * 2); - 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(); - } -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java deleted file mode 100644 index 7e4cdf2..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ruoyi.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.ruoyi.gateway.handler.ValidateCodeHandler; - -/** - * 路由配置信息 - * - * @author ruoyi - */ -@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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java deleted file mode 100644 index 7266730..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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 -> !"ruoyi-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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/CaptchaProperties.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/CaptchaProperties.java deleted file mode 100644 index dea2e16..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/CaptchaProperties.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java deleted file mode 100644 index 7df1171..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreWhiteProperties.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/XssProperties.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/XssProperties.java deleted file mode 100644 index 834188b..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/XssProperties.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/constant/MessagesConstant.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/constant/MessagesConstant.java deleted file mode 100644 index 5763dbc..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/constant/MessagesConstant.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.ruoyi.gateway.constant; - -import com.ruoyi.gateway.filter.AuthFilter; -import org.slf4j.LoggerFactory; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.web.server.ServerWebExchange; - -import java.util.List; -import java.util.Locale; -import java.util.logging.Logger; - -public class MessagesConstant { - private static final org.slf4j.Logger log = LoggerFactory.getLogger(MessagesConstant.class); - /**令牌不能为空*/ - public static final String MESSAGES_TOKE_EMPTY_EN="Token cannot be empty"; - public static final String MESSAGES_TOKE_EMPTY_ZH="令牌不能为"; - /**令牌已过期或验证不正确*/ - public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_EN="Token expired or incorrectly verified"; - public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_ZH="令牌已过期或验证不正确"; - /**登录状态已过期*/ - public static final String MESSAGES_LOGIN_STATUS_EXPIRED_EN="Login status has expired"; - public static final String MESSAGES_LOGIN_STATUS_EXPIRED_ZH="登录状态已过期"; - /**令牌验证失败*/ - public static final String MESSAGES_TOKE_VERIFICATION_FAILED_EN="Token verification failed"; - public static final String MESSAGES_TOKE_VERIFICATION_FAILED_ZH="令牌验证失败"; - /**请求地址不允许访问*/ - public static final String MESSAGES_REQUEST_ADDRESS_EN="The requested address does not allow access"; - public static final String MESSAGES_REQUEST_ADDRESS_ZH="请求地址不允许访问"; - /**服务未找到*/ - public static final String MESSAGES_SERVICE_NOT_FOUND_EN="Service not found"; - public static final String MESSAGES_SERVICE_NOT_FOUND_ZH="服务未找到"; - /**内部服务器错误*/ - public static final String MESSAGES_INTERNAL_SERVER_ERROR_EN="Internal server error"; - public static final String MESSAGES_INTERNAL_SERVER_ERROR_ZH="内部服务器错误"; - - - /**语言类型 中文*/ - public static final String Language_ZH="zh"; - /**语言类型 英文*/ - public static final String Language_EN="en"; - /**语言类型 key*/ - public static final String HEADER_KEY="yisailanguage"; - - - /** - * 国际化处理 - * @param exchange - * @return - */ - public static String getMessages(ServerWebExchange exchange, String zh_msg, String en_msg) { - //给一个默认值 - String msg=en_msg; - ServerHttpRequest request = exchange.getRequest(); - if (request.getHeaders().containsKey(MessagesConstant.HEADER_KEY)) { - String zh = exchange.getRequest().getHeaders().getFirst(MessagesConstant.HEADER_KEY); - if (MessagesConstant.Language_ZH.equals(zh)) { - msg = zh_msg; - } else { - msg = en_msg; - } - }else { - String s = request.getHeaders().get("accept-language").get(0).split(",")[0].split("-")[0]; - log.info("获取头部的accept-language值:",request.getHeaders().get("accept-language")); - log.info("获取头部的语言:{}",s); - if (MessagesConstant.Language_ZH.equals(s)) { - msg = zh_msg; - } else { - msg = en_msg; - } - - - } - - - return msg; - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java deleted file mode 100644 index 49a8ce1..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.ruoyi.gateway.filter; - -import com.ruoyi.gateway.constant.MessagesConstant; -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.ruoyi.common.core.constant.CacheConstants; -import com.ruoyi.common.core.constant.HttpStatus; -import com.ruoyi.common.core.constant.SecurityConstants; -import com.ruoyi.common.core.constant.TokenConstants; -import com.ruoyi.common.core.utils.JwtUtils; -import com.ruoyi.common.core.utils.ServletUtils; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.redis.service.RedisService; -import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties; -import io.jsonwebtoken.Claims; -import reactor.core.publisher.Mono; - -/** - * 网关鉴权 - * - * @author ruoyi - */ -@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, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_EMPTY_ZH,MessagesConstant.MESSAGES_TOKE_EMPTY_EN)); - } - Claims claims = JwtUtils.parseToken(token); - if (claims == null) - { - return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_ZH,MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_EN)); - } - String userkey = JwtUtils.getUserKey(claims); - boolean islogin = redisService.hasKey(getTokenKey(userkey)); - if (!islogin) - { - return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_ZH,MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_EN)); - } - String userid = JwtUtils.getUserId(claims); - String username = JwtUtils.getUserName(claims); - if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) - { - return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_ZH,MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_EN)); - } - - // 设置用户信息到请求 - 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; - } -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java deleted file mode 100644 index d5ce7cd..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.ruoyi.gateway.filter; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import com.ruoyi.gateway.constant.MessagesConstant; -import org.springframework.cloud.gateway.filter.GatewayFilter; -import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; -import org.springframework.stereotype.Component; -import com.ruoyi.common.core.utils.ServletUtils; - -/** - * 黑名单过滤器 - * - * @author ruoyi - */ -@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(), MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_REQUEST_ADDRESS_ZH,MessagesConstant.MESSAGES_REQUEST_ADDRESS_EN)); - } - - 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/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java deleted file mode 100644 index 94c6cb5..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/CacheRequestFilter.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.ruoyi.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 ruoyi - */ -@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; - } - } -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java deleted file mode 100644 index 93e366e..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ruoyi.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.ruoyi.common.core.utils.ServletUtils; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.gateway.config.properties.CaptchaProperties; -import com.ruoyi.gateway.service.ValidateCodeService; -import reactor.core.publisher.Flux; - -/** - * 验证码过滤器 - * - * @author ruoyi - */ -@Component -public class ValidateCodeFilter extends AbstractGatewayFilterFactory -{ - private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" }; - - @Autowired - private ValidateCodeService validateCodeService; - - @Autowired - private CaptchaProperties captchaProperties; - - private static final String CODE = "code"; - - private static final String UUID = "uuid"; - - @Override - public GatewayFilter apply(Object config) - { - return (exchange, chain) -> { - ServerHttpRequest request = exchange.getRequest(); - - // 非登录/注册请求或验证码关闭,不处理 - if (!StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) - { - return chain.filter(exchange); - } - - try - { - String rspStr = resolveBodyFromRequest(request); - JSONObject obj = JSON.parseObject(rspStr); - validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID)); - } - catch (Exception e) - { - return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage()); - } - return chain.filter(exchange); - }; - } - - private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) - { - // 获取请求体 - Flux body = serverHttpRequest.getBody(); - AtomicReference bodyRef = new AtomicReference<>(); - body.subscribe(buffer -> { - CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); - DataBufferUtils.release(buffer); - bodyRef.set(charBuffer.toString()); - }); - return bodyRef.get(); - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/XssFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/XssFilter.java deleted file mode 100644 index bc93e3a..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/XssFilter.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.ruoyi.gateway.filter; - -import java.nio.charset.StandardCharsets; -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.DataBuffer; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.core.io.buffer.NettyDataBufferFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -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 com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.core.utils.html.EscapeUtil; -import com.ruoyi.gateway.config.properties.XssProperties; -import io.netty.buffer.ByteBufAllocator; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * 跨站脚本过滤器 - * - * @author ruoyi - */ -@Component -@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") -public class XssFilter implements GlobalFilter, Ordered -{ - // 跨站脚本的 xss 配置,nacos自行添加 - @Autowired - private XssProperties xss; - - @Override - public Mono 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); - } - // 非json类型,不过滤 - if (!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 getBody() - { - Flux 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); - // 防xss攻击过滤 - bodyStr = EscapeUtil.clean(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()); - // 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length - httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); - httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); - return httpHeaders; - } - - }; - return serverHttpRequestDecorator; - } - - /** - * 是否是Json请求 - * - * @param exchange HTTP请求 - */ - public boolean isJsonRequest(ServerWebExchange exchange) - { - String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); - return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); - } - - @Override - public int getOrder() - { - return -100; - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java deleted file mode 100644 index 2af637e..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.ruoyi.gateway.handler; - -import com.ruoyi.gateway.constant.MessagesConstant; -import org.springframework.cloud.gateway.support.NotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; -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 com.ruoyi.common.core.utils.ServletUtils; -import reactor.core.publisher.Mono; - -/** - * 网关统一异常处理 - * - * @author ruoyi - */ -@Order(-1) -@Configuration -public class GatewayExceptionHandler implements ErrorWebExceptionHandler -{ - private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); - - @Override - public Mono handle(ServerWebExchange exchange, Throwable ex) - { - ServerHttpResponse response = exchange.getResponse(); - - if (exchange.getResponse().isCommitted()) - { - return Mono.error(ex); - } - - String msg; - - if (ex instanceof NotFoundException) - { - /**服务未找到*/ - msg =MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_ZH,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_EN); - - } - else if (ex instanceof ResponseStatusException) - { - ResponseStatusException responseStatusException = (ResponseStatusException) ex; - msg = responseStatusException.getMessage(); - } - else - { - /**内部服务器错误*/ - //msg="内部服务器错误"; - msg =MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_ZH,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_EN); - } - - log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); - - return ServletUtils.webFluxResponseWriter(response, msg); - } - - -} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java deleted file mode 100644 index 1b496e2..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SentinelFallbackHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ruoyi.gateway.handler; - -import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.ruoyi.common.core.utils.ServletUtils; -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 writeResponse(ServerResponse response, ServerWebExchange exchange) - { - return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); - } - - @Override - public Mono handle(ServerWebExchange exchange, Throwable ex) - { - 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 handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) - { - return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java deleted file mode 100644 index af1e7ad..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ruoyi.gateway.handler; - -import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Mono; -import springfox.documentation.swagger.web.SecurityConfiguration; -import springfox.documentation.swagger.web.SecurityConfigurationBuilder; -import springfox.documentation.swagger.web.SwaggerResourcesProvider; -import springfox.documentation.swagger.web.UiConfiguration; -import springfox.documentation.swagger.web.UiConfigurationBuilder; - -@RestController -@RequestMapping("/swagger-resources") -public class SwaggerHandler -{ - @Autowired(required = false) - private SecurityConfiguration securityConfiguration; - - @Autowired(required = false) - private UiConfiguration uiConfiguration; - - private final SwaggerResourcesProvider swaggerResources; - - @Autowired - public SwaggerHandler(SwaggerResourcesProvider swaggerResources) - { - this.swaggerResources = swaggerResources; - } - - @GetMapping("/configuration/security") - public Mono> securityConfiguration() - { - return Mono.just(new ResponseEntity<>( - Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), - HttpStatus.OK)); - } - - @GetMapping("/configuration/ui") - public Mono> uiConfiguration() - { - return Mono.just(new ResponseEntity<>( - Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); - } - - @SuppressWarnings("rawtypes") - @GetMapping("") - public Mono swaggerResources() - { - return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java deleted file mode 100644 index ef197de..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ruoyi.gateway.handler; - -import java.io.IOException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.HandlerFunction; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import com.ruoyi.common.core.exception.CaptchaException; -import com.ruoyi.common.core.web.domain.AjaxResult; -import com.ruoyi.gateway.service.ValidateCodeService; -import reactor.core.publisher.Mono; - -/** - * 验证码获取 - * - * @author ruoyi - */ -@Component -public class ValidateCodeHandler implements HandlerFunction -{ - @Autowired - private ValidateCodeService validateCodeService; - - @Override - public Mono handle(ServerRequest serverRequest) - { - AjaxResult ajax; - try - { - ajax = validateCodeService.createCaptcha(); - } - catch (CaptchaException | IOException e) - { - return Mono.error(e); - } - return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax)); - } -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java deleted file mode 100644 index 615fb07..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ruoyi.gateway.service; - -import java.io.IOException; -import com.ruoyi.common.core.exception.CaptchaException; -import com.ruoyi.common.core.web.domain.AjaxResult; - -/** - * 验证码处理 - * - * @author ruoyi - */ -public interface ValidateCodeService -{ - /** - * 生成验证码 - */ - public AjaxResult createCaptcha() throws IOException, CaptchaException; - - /** - * 校验验证码 - */ - public void checkCaptcha(String key, String value) throws CaptchaException; -} diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java deleted file mode 100644 index 8d2c180..0000000 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.ruoyi.gateway.service.impl; - -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import javax.annotation.Resource; -import javax.imageio.ImageIO; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.FastByteArrayOutputStream; -import com.google.code.kaptcha.Producer; -import com.ruoyi.common.core.constant.Constants; -import com.ruoyi.common.core.exception.CaptchaException; -import com.ruoyi.common.core.utils.StringUtils; -import com.ruoyi.common.core.utils.sign.Base64; -import com.ruoyi.common.core.utils.uuid.IdUtils; -import com.ruoyi.common.core.web.domain.AjaxResult; -import com.ruoyi.common.redis.service.RedisService; -import com.ruoyi.gateway.config.properties.CaptchaProperties; -import com.ruoyi.gateway.service.ValidateCodeService; - -/** - * 验证码实现处理 - * - * @author ruoyi - */ -@Service -public class ValidateCodeServiceImpl implements ValidateCodeService -{ - @Resource(name = "captchaProducer") - private Producer captchaProducer; - - @Resource(name = "captchaProducerMath") - private Producer captchaProducerMath; - - @Autowired - private RedisService redisService; - - @Autowired - private CaptchaProperties captchaProperties; - - /** - * 生成验证码 - */ - @Override - public AjaxResult createCaptcha() throws IOException, CaptchaException - { - AjaxResult ajax = AjaxResult.success(); - boolean captchaOnOff = captchaProperties.getEnabled(); - ajax.put("captchaOnOff", captchaOnOff); - if (!captchaOnOff) - { - return ajax; - } - - // 保存验证码信息 - String uuid = IdUtils.simpleUUID(); - String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; - - String capStr = null, code = null; - BufferedImage image = null; - - String captchaType = captchaProperties.getType(); - // 生成验证码 - if ("math".equals(captchaType)) - { - String capText = captchaProducerMath.createText(); - capStr = capText.substring(0, capText.lastIndexOf("@")); - code = capText.substring(capText.lastIndexOf("@") + 1); - image = captchaProducerMath.createImage(capStr); - } - else if ("char".equals(captchaType)) - { - capStr = code = captchaProducer.createText(); - image = captchaProducer.createImage(capStr); - } - - redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); - // 转换流信息写出 - FastByteArrayOutputStream os = new FastByteArrayOutputStream(); - try - { - ImageIO.write(image, "jpg", os); - } - catch (IOException e) - { - return AjaxResult.error(e.getMessage()); - } - - ajax.put("uuid", uuid); - ajax.put("img", Base64.encode(os.toByteArray())); - return ajax; - } - - /** - * 校验验证码 - */ - @Override - public void checkCaptcha(String code, String uuid) throws CaptchaException - { - if (StringUtils.isEmpty(code)) - { - throw new CaptchaException("验证码不能为空"); - } - if (StringUtils.isEmpty(uuid)) - { - throw new CaptchaException("验证码已失效"); - } - String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; - String captcha = redisService.getCacheObject(verifyKey); - redisService.deleteObject(verifyKey); - - if (!code.equalsIgnoreCase(captcha)) - { - throw new CaptchaException("验证码错误"); - } - } -} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/RuoYiGatewayApplication.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/RuoYiGatewayApplication.java new file mode 100644 index 0000000..493c072 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/RuoYiGatewayApplication.java @@ -0,0 +1,29 @@ +package com.yisai.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 网关启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) +public class RuoYiGatewayApplication +{ + public static void main(String[] args) + { + SpringApplication.run(RuoYiGatewayApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/config/CaptchaConfig.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/CaptchaConfig.java new file mode 100644 index 0000000..9743e42 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.yisai.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 ruoyi + */ +@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.ruoyi.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; + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/config/GatewayConfig.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/GatewayConfig.java new file mode 100644 index 0000000..5473e51 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/GatewayConfig.java @@ -0,0 +1,23 @@ +package com.yisai.gateway.config; + +import com.yisai.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(); + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/config/KaptchaTextCreator.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/KaptchaTextCreator.java new file mode 100644 index 0000000..143d95d --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/KaptchaTextCreator.java @@ -0,0 +1,75 @@ +package com.yisai.gateway.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +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 = (int) Math.round(Math.random() * 2); + 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(); + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/config/RouterFunctionConfiguration.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/RouterFunctionConfiguration.java new file mode 100644 index 0000000..d40e85e --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/RouterFunctionConfiguration.java @@ -0,0 +1,31 @@ +package com.yisai.gateway.config; + +import com.yisai.gateway.handler.ValidateCodeHandler; +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; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@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/ruoyi-gateway/src/main/java/com/yisai/gateway/config/SwaggerProvider.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/SwaggerProvider.java new file mode 100644 index 0000000..bd21235 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/SwaggerProvider.java @@ -0,0 +1,79 @@ +package com.yisai.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 ruoyi + */ +@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 -> !"ruoyi-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/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/CaptchaProperties.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..be2642a --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/CaptchaProperties.java @@ -0,0 +1,46 @@ +package com.yisai.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 ruoyi + */ +@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/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/IgnoreWhiteProperties.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 0000000..7aaa568 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/IgnoreWhiteProperties.java @@ -0,0 +1,33 @@ +package com.yisai.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 ruoyi + */ +@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/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/XssProperties.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..aa62aa0 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/config/properties/XssProperties.java @@ -0,0 +1,48 @@ +package com.yisai.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 ruoyi + */ +@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/ruoyi-gateway/src/main/java/com/yisai/gateway/constant/MessagesConstant.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/constant/MessagesConstant.java new file mode 100644 index 0000000..ac4ab90 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/constant/MessagesConstant.java @@ -0,0 +1,72 @@ +package com.yisai.gateway.constant; + +import org.slf4j.LoggerFactory; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; + +public class MessagesConstant { + private static final org.slf4j.Logger log = LoggerFactory.getLogger(MessagesConstant.class); + /**令牌不能为空*/ + public static final String MESSAGES_TOKE_EMPTY_EN="Token cannot be empty"; + public static final String MESSAGES_TOKE_EMPTY_ZH="令牌不能为"; + /**令牌已过期或验证不正确*/ + public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_EN="Token expired or incorrectly verified"; + public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_ZH="令牌已过期或验证不正确"; + /**登录状态已过期*/ + public static final String MESSAGES_LOGIN_STATUS_EXPIRED_EN="Login status has expired"; + public static final String MESSAGES_LOGIN_STATUS_EXPIRED_ZH="登录状态已过期"; + /**令牌验证失败*/ + public static final String MESSAGES_TOKE_VERIFICATION_FAILED_EN="Token verification failed"; + public static final String MESSAGES_TOKE_VERIFICATION_FAILED_ZH="令牌验证失败"; + /**请求地址不允许访问*/ + public static final String MESSAGES_REQUEST_ADDRESS_EN="The requested address does not allow access"; + public static final String MESSAGES_REQUEST_ADDRESS_ZH="请求地址不允许访问"; + /**服务未找到*/ + public static final String MESSAGES_SERVICE_NOT_FOUND_EN="Service not found"; + public static final String MESSAGES_SERVICE_NOT_FOUND_ZH="服务未找到"; + /**内部服务器错误*/ + public static final String MESSAGES_INTERNAL_SERVER_ERROR_EN="Internal server error"; + public static final String MESSAGES_INTERNAL_SERVER_ERROR_ZH="内部服务器错误"; + + + /**语言类型 中文*/ + public static final String Language_ZH="zh"; + /**语言类型 英文*/ + public static final String Language_EN="en"; + /**语言类型 key*/ + public static final String HEADER_KEY="yisailanguage"; + + + /** + * 国际化处理 + * @param exchange + * @return + */ + public static String getMessages(ServerWebExchange exchange, String zh_msg, String en_msg) { + //给一个默认值 + String msg=en_msg; + ServerHttpRequest request = exchange.getRequest(); + if (request.getHeaders().containsKey(MessagesConstant.HEADER_KEY)) { + String zh = exchange.getRequest().getHeaders().getFirst(MessagesConstant.HEADER_KEY); + if (MessagesConstant.Language_ZH.equals(zh)) { + msg = zh_msg; + } else { + msg = en_msg; + } + }else { + String s = request.getHeaders().get("accept-language").get(0).split(",")[0].split("-")[0]; + log.info("获取头部的accept-language值:",request.getHeaders().get("accept-language")); + log.info("获取头部的语言:{}",s); + if (MessagesConstant.Language_ZH.equals(s)) { + msg = zh_msg; + } else { + msg = en_msg; + } + + + } + + + return msg; + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/AuthFilter.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..f26f64d --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/AuthFilter.java @@ -0,0 +1,138 @@ +package com.yisai.gateway.filter; + +import com.yisai.gateway.constant.MessagesConstant; +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.ruoyi.common.core.constant.CacheConstants; +import com.ruoyi.common.core.constant.HttpStatus; +import com.ruoyi.common.core.constant.SecurityConstants; +import com.ruoyi.common.core.constant.TokenConstants; +import com.ruoyi.common.core.utils.JwtUtils; +import com.ruoyi.common.core.utils.ServletUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.redis.service.RedisService; +import com.yisai.gateway.config.properties.IgnoreWhiteProperties; +import io.jsonwebtoken.Claims; +import reactor.core.publisher.Mono; + +/** + * 网关鉴权 + * + * @author ruoyi + */ +@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, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_EMPTY_ZH,MessagesConstant.MESSAGES_TOKE_EMPTY_EN)); + } + Claims claims = JwtUtils.parseToken(token); + if (claims == null) + { + return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_ZH,MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_EN)); + } + String userkey = JwtUtils.getUserKey(claims); + boolean islogin = redisService.hasKey(getTokenKey(userkey)); + if (!islogin) + { + return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_ZH,MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_EN)); + } + String userid = JwtUtils.getUserId(claims); + String username = JwtUtils.getUserName(claims); + if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) + { + return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_ZH,MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_EN)); + } + + // 设置用户信息到请求 + 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; + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/BlackListUrlFilter.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/BlackListUrlFilter.java new file mode 100644 index 0000000..fb1ddf6 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/BlackListUrlFilter.java @@ -0,0 +1,67 @@ +package com.yisai.gateway.filter; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import com.yisai.gateway.constant.MessagesConstant; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.utils.ServletUtils; + +/** + * 黑名单过滤器 + * + * @author ruoyi + */ +@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(), MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_REQUEST_ADDRESS_ZH,MessagesConstant.MESSAGES_REQUEST_ADDRESS_EN)); + } + + 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/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/CacheRequestFilter.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/CacheRequestFilter.java new file mode 100644 index 0000000..6553ae5 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/CacheRequestFilter.java @@ -0,0 +1,87 @@ +package com.yisai.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 ruoyi + */ +@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; + } + } +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/ValidateCodeFilter.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/ValidateCodeFilter.java new file mode 100644 index 0000000..d00f544 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/ValidateCodeFilter.java @@ -0,0 +1,80 @@ +package com.yisai.gateway.filter; + +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +import com.yisai.gateway.config.properties.CaptchaProperties; +import com.yisai.gateway.service.ValidateCodeService; +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.ruoyi.common.core.utils.ServletUtils; +import com.ruoyi.common.core.utils.StringUtils; +import reactor.core.publisher.Flux; + +/** + * 验证码过滤器 + * + * @author ruoyi + */ +@Component +public class ValidateCodeFilter extends AbstractGatewayFilterFactory +{ + private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" }; + + @Autowired + private ValidateCodeService validateCodeService; + + @Autowired + private CaptchaProperties captchaProperties; + + private static final String CODE = "code"; + + private static final String UUID = "uuid"; + + @Override + public GatewayFilter apply(Object config) + { + return (exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + + // 非登录/注册请求或验证码关闭,不处理 + if (!StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) + { + return chain.filter(exchange); + } + + try + { + String rspStr = resolveBodyFromRequest(request); + JSONObject obj = JSON.parseObject(rspStr); + validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID)); + } + catch (Exception e) + { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage()); + } + return chain.filter(exchange); + }; + } + + private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) + { + // 获取请求体 + Flux body = serverHttpRequest.getBody(); + AtomicReference bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + }); + return bodyRef.get(); + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/XssFilter.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/XssFilter.java new file mode 100644 index 0000000..8993ec1 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/filter/XssFilter.java @@ -0,0 +1,124 @@ +package com.yisai.gateway.filter; + +import java.nio.charset.StandardCharsets; +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.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.core.io.buffer.NettyDataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +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 com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.html.EscapeUtil; +import com.yisai.gateway.config.properties.XssProperties; +import io.netty.buffer.ByteBufAllocator; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * 跨站脚本过滤器 + * + * @author ruoyi + */ +@Component +@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") +public class XssFilter implements GlobalFilter, Ordered +{ + // 跨站脚本的 xss 配置,nacos自行添加 + @Autowired + private XssProperties xss; + + @Override + public Mono 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); + } + // 非json类型,不过滤 + if (!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 getBody() + { + Flux 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); + // 防xss攻击过滤 + bodyStr = EscapeUtil.clean(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()); + // 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length + httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + return httpHeaders; + } + + }; + return serverHttpRequestDecorator; + } + + /** + * 是否是Json请求 + * + * @param exchange HTTP请求 + */ + public boolean isJsonRequest(ServerWebExchange exchange) + { + String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + + @Override + public int getOrder() + { + return -100; + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/GatewayExceptionHandler.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..7288635 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,63 @@ +package com.yisai.gateway.handler; + +import com.yisai.gateway.constant.MessagesConstant; +import org.springframework.cloud.gateway.support.NotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +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 com.ruoyi.common.core.utils.ServletUtils; +import reactor.core.publisher.Mono; + +/** + * 网关统一异常处理 + * + * @author ruoyi + */ +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) + { + ServerHttpResponse response = exchange.getResponse(); + + if (exchange.getResponse().isCommitted()) + { + return Mono.error(ex); + } + + String msg; + + if (ex instanceof NotFoundException) + { + /**服务未找到*/ + msg =MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_ZH,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_EN); + + } + else if (ex instanceof ResponseStatusException) + { + ResponseStatusException responseStatusException = (ResponseStatusException) ex; + msg = responseStatusException.getMessage(); + } + else + { + /**内部服务器错误*/ + //msg="内部服务器错误"; + msg =MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_ZH,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_EN); + } + + log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return ServletUtils.webFluxResponseWriter(response, msg); + } + + +} \ No newline at end of file diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SentinelFallbackHandler.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SentinelFallbackHandler.java new file mode 100644 index 0000000..06b3ce1 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SentinelFallbackHandler.java @@ -0,0 +1,41 @@ +package com.yisai.gateway.handler; + +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.ruoyi.common.core.utils.ServletUtils; +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 writeResponse(ServerResponse response, ServerWebExchange exchange) + { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); + } + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) + { + 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 handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) + { + return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SwaggerHandler.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SwaggerHandler.java new file mode 100644 index 0000000..8c7418a --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/SwaggerHandler.java @@ -0,0 +1,56 @@ +package com.yisai.gateway.handler; + +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import springfox.documentation.swagger.web.SecurityConfiguration; +import springfox.documentation.swagger.web.SecurityConfigurationBuilder; +import springfox.documentation.swagger.web.SwaggerResourcesProvider; +import springfox.documentation.swagger.web.UiConfiguration; +import springfox.documentation.swagger.web.UiConfigurationBuilder; + +@RestController +@RequestMapping("/swagger-resources") +public class SwaggerHandler +{ + @Autowired(required = false) + private SecurityConfiguration securityConfiguration; + + @Autowired(required = false) + private UiConfiguration uiConfiguration; + + private final SwaggerResourcesProvider swaggerResources; + + @Autowired + public SwaggerHandler(SwaggerResourcesProvider swaggerResources) + { + this.swaggerResources = swaggerResources; + } + + @GetMapping("/configuration/security") + public Mono> securityConfiguration() + { + return Mono.just(new ResponseEntity<>( + Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), + HttpStatus.OK)); + } + + @GetMapping("/configuration/ui") + public Mono> uiConfiguration() + { + return Mono.just(new ResponseEntity<>( + Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); + } + + @SuppressWarnings("rawtypes") + @GetMapping("") + public Mono swaggerResources() + { + return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/ValidateCodeHandler.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/ValidateCodeHandler.java new file mode 100644 index 0000000..aa95afa --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/handler/ValidateCodeHandler.java @@ -0,0 +1,41 @@ +package com.yisai.gateway.handler; + +import java.io.IOException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.HandlerFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import com.ruoyi.common.core.exception.CaptchaException; +import com.ruoyi.common.core.web.domain.AjaxResult; +import com.yisai.gateway.service.ValidateCodeService; +import reactor.core.publisher.Mono; + +/** + * 验证码获取 + * + * @author ruoyi + */ +@Component +public class ValidateCodeHandler implements HandlerFunction +{ + @Autowired + private ValidateCodeService validateCodeService; + + @Override + public Mono handle(ServerRequest serverRequest) + { + AjaxResult ajax; + try + { + ajax = validateCodeService.createCaptcha(); + } + catch (CaptchaException | IOException e) + { + return Mono.error(e); + } + return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax)); + } +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/service/ValidateCodeService.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/service/ValidateCodeService.java new file mode 100644 index 0000000..13ef4a9 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/service/ValidateCodeService.java @@ -0,0 +1,23 @@ +package com.yisai.gateway.service; + +import java.io.IOException; +import com.ruoyi.common.core.exception.CaptchaException; +import com.ruoyi.common.core.web.domain.AjaxResult; + +/** + * 验证码处理 + * + * @author ruoyi + */ +public interface ValidateCodeService +{ + /** + * 生成验证码 + */ + public AjaxResult createCaptcha() throws IOException, CaptchaException; + + /** + * 校验验证码 + */ + public void checkCaptcha(String key, String value) throws CaptchaException; +} diff --git a/ruoyi-gateway/src/main/java/com/yisai/gateway/service/impl/ValidateCodeServiceImpl.java b/ruoyi-gateway/src/main/java/com/yisai/gateway/service/impl/ValidateCodeServiceImpl.java new file mode 100644 index 0000000..49eaff3 --- /dev/null +++ b/ruoyi-gateway/src/main/java/com/yisai/gateway/service/impl/ValidateCodeServiceImpl.java @@ -0,0 +1,118 @@ +package com.yisai.gateway.service.impl; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.FastByteArrayOutputStream; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.core.constant.Constants; +import com.ruoyi.common.core.exception.CaptchaException; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.sign.Base64; +import com.ruoyi.common.core.utils.uuid.IdUtils; +import com.ruoyi.common.core.web.domain.AjaxResult; +import com.ruoyi.common.redis.service.RedisService; +import com.yisai.gateway.config.properties.CaptchaProperties; +import com.yisai.gateway.service.ValidateCodeService; + +/** + * 验证码实现处理 + * + * @author ruoyi + */ +@Service +public class ValidateCodeServiceImpl implements ValidateCodeService +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisService redisService; + + @Autowired + private CaptchaProperties captchaProperties; + + /** + * 生成验证码 + */ + @Override + public AjaxResult createCaptcha() throws IOException, CaptchaException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaOnOff = captchaProperties.getEnabled(); + ajax.put("captchaOnOff", captchaOnOff); + if (!captchaOnOff) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + String captchaType = captchaProperties.getType(); + // 生成验证码 + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } + + /** + * 校验验证码 + */ + @Override + public void checkCaptcha(String code, String uuid) throws CaptchaException + { + if (StringUtils.isEmpty(code)) + { + throw new CaptchaException("验证码不能为空"); + } + if (StringUtils.isEmpty(uuid)) + { + throw new CaptchaException("验证码已失效"); + } + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String captcha = redisService.getCacheObject(verifyKey); + redisService.deleteObject(verifyKey); + + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException("验证码错误"); + } + } +}