初始化
commit
280574caf2
|
@ -0,0 +1,47 @@
|
||||||
|
######################################################################
|
||||||
|
# Build Tools
|
||||||
|
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# IDE
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### JRebel ###
|
||||||
|
rebel.xml
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/*
|
||||||
|
nbbuild/
|
||||||
|
dist/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
target/
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Others
|
||||||
|
*.log
|
||||||
|
*.xml.versionsBackup
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
!*/build/*.java
|
||||||
|
!*/build/*.html
|
||||||
|
!*/build/*.xml
|
|
@ -0,0 +1,21 @@
|
||||||
|
FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:17-8.6
|
||||||
|
|
||||||
|
|
||||||
|
# 暴露端口号
|
||||||
|
EXPOSE 8078/tcp
|
||||||
|
|
||||||
|
|
||||||
|
# 挂载目录位置
|
||||||
|
VOLUME /home/logs/grail-gateway
|
||||||
|
|
||||||
|
#构造 复制外部文件到docker 内部
|
||||||
|
COPY /target/grail-gateway.jar /home/app.jar
|
||||||
|
|
||||||
|
# 工作目录 exec -it 进来就是默认这个目录
|
||||||
|
WORKDIR /home
|
||||||
|
|
||||||
|
# 指定东八区
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > .etc.timezone
|
||||||
|
|
||||||
|
# 启动java程序
|
||||||
|
CMD ["java","-Dfile.encoding=UTF-8","-jar","/home/app.jar"]
|
|
@ -0,0 +1,113 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.grail</groupId>
|
||||||
|
<artifactId>grail</artifactId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<version>3.6.3</version>
|
||||||
|
|
||||||
|
<artifactId>grail-gateway</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<description>
|
||||||
|
grail-gateway网关模块11111
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringClou1d Gateway -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Alibaba Nacos -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Alibaba Nacos Config -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Alibaba Sentinel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Alibaba Sentinel Gateway -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sentinel Datasource Nacos -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot Actuator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Loadbalancer -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--验证码 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.penggle</groupId>
|
||||||
|
<artifactId>kaptcha</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- grail Common Redis-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.grail</groupId>
|
||||||
|
<artifactId>grail-common-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Swagger -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger-ui</artifactId>
|
||||||
|
<version>${swagger.fox.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger2</artifactId>
|
||||||
|
<version>${swagger.fox.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.grail.gateway;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关启动程序
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||||
|
public class GrailGatewayApplication
|
||||||
|
{
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
SpringApplication.run(GrailGatewayApplication.class, args);
|
||||||
|
System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ ");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@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.grail.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.grail.gateway.config;
|
||||||
|
|
||||||
|
import com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class GatewayConfig
|
||||||
|
{
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
|
||||||
|
{
|
||||||
|
return new SentinelFallbackHandler();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.grail.gateway.config;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码文本生成器
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
public class KaptchaTextCreator extends DefaultTextCreator
|
||||||
|
{
|
||||||
|
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
Integer result = 0;
|
||||||
|
Random random = new Random();
|
||||||
|
int x = random.nextInt(10);
|
||||||
|
int y = random.nextInt(10);
|
||||||
|
StringBuilder suChinese = new StringBuilder();
|
||||||
|
int randomoperands = random.nextInt(3);
|
||||||
|
if (randomoperands == 0)
|
||||||
|
{
|
||||||
|
result = x * y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("*");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
else if (randomoperands == 1)
|
||||||
|
{
|
||||||
|
if ((x != 0) && y % x == 0)
|
||||||
|
{
|
||||||
|
result = y / x;
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
suChinese.append("/");
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = x + y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("+");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (randomoperands == 2)
|
||||||
|
{
|
||||||
|
if (x >= y)
|
||||||
|
{
|
||||||
|
result = x - y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("-");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = y - x;
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
suChinese.append("-");
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = x + y;
|
||||||
|
suChinese.append(CNUMBERS[x]);
|
||||||
|
suChinese.append("+");
|
||||||
|
suChinese.append(CNUMBERS[y]);
|
||||||
|
}
|
||||||
|
suChinese.append("=?@" + result);
|
||||||
|
return suChinese.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.grail.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.grail.gateway.handler.ValidateCodeHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由配置信息
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@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<SwaggerResource> get()
|
||||||
|
{
|
||||||
|
List<SwaggerResource> resourceList = new ArrayList<>();
|
||||||
|
List<String> 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 -> !"grail-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/");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "security.ignore")
|
||||||
|
public class IgnoreWhiteProperties
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 放行白名单配置,网关不校验此处的白名单
|
||||||
|
*/
|
||||||
|
private List<String> whites = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<String> getWhites()
|
||||||
|
{
|
||||||
|
return whites;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWhites(List<String> whites)
|
||||||
|
{
|
||||||
|
this.whites = whites;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "security.xss")
|
||||||
|
public class XssProperties
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Xss开关
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除路径
|
||||||
|
*/
|
||||||
|
private List<String> excludeUrls = new ArrayList<>();
|
||||||
|
|
||||||
|
public Boolean getEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExcludeUrls()
|
||||||
|
{
|
||||||
|
return excludeUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExcludeUrls(List<String> excludeUrls)
|
||||||
|
{
|
||||||
|
this.excludeUrls = excludeUrls;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.grail.gateway.filter;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import com.grail.common.core.constant.CacheConstants;
|
||||||
|
import com.grail.common.core.constant.HttpStatus;
|
||||||
|
import com.grail.common.core.constant.SecurityConstants;
|
||||||
|
import com.grail.common.core.constant.TokenConstants;
|
||||||
|
import com.grail.common.core.utils.JwtUtils;
|
||||||
|
import com.grail.common.core.utils.ServletUtils;
|
||||||
|
import com.grail.common.core.utils.StringUtils;
|
||||||
|
import com.grail.common.redis.service.RedisService;
|
||||||
|
import com.grail.gateway.config.properties.IgnoreWhiteProperties;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关鉴权
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@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<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
|
||||||
|
{
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
ServerHttpRequest.Builder mutate = request.mutate();
|
||||||
|
|
||||||
|
String url = request.getURI().getPath();
|
||||||
|
// 跳过不需要验证的路径
|
||||||
|
if (StringUtils.matches(url, ignoreWhite.getWhites()))
|
||||||
|
{
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
String token = getToken(request);
|
||||||
|
if (StringUtils.isEmpty(token))
|
||||||
|
{
|
||||||
|
return unauthorizedResponse(exchange, "令牌不能为空");
|
||||||
|
}
|
||||||
|
Claims claims = JwtUtils.parseToken(token);
|
||||||
|
if (claims == null)
|
||||||
|
{
|
||||||
|
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
|
||||||
|
}
|
||||||
|
String userkey = JwtUtils.getUserKey(claims);
|
||||||
|
boolean islogin = redisService.hasKey(getTokenKey(userkey));
|
||||||
|
if (!islogin)
|
||||||
|
{
|
||||||
|
return unauthorizedResponse(exchange, "登录状态已过期");
|
||||||
|
}
|
||||||
|
String userid = JwtUtils.getUserId(claims);
|
||||||
|
String username = JwtUtils.getUserName(claims);
|
||||||
|
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
|
||||||
|
{
|
||||||
|
return unauthorizedResponse(exchange, "令牌验证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户信息到请求
|
||||||
|
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
|
||||||
|
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
|
||||||
|
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
|
||||||
|
// 内部请求来源参数清除
|
||||||
|
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
|
||||||
|
return chain.filter(exchange.mutate().request(mutate.build()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String valueStr = value.toString();
|
||||||
|
String valueEncode = ServletUtils.urlEncode(valueStr);
|
||||||
|
mutate.header(name, valueEncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeHeader(ServerHttpRequest.Builder mutate, String name)
|
||||||
|
{
|
||||||
|
mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<Void> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.grail.gateway.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||||
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.grail.common.core.utils.ServletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单过滤器
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public GatewayFilter apply(Config config)
|
||||||
|
{
|
||||||
|
return (exchange, chain) -> {
|
||||||
|
|
||||||
|
String url = exchange.getRequest().getURI().getPath();
|
||||||
|
if (config.matchBlacklist(url))
|
||||||
|
{
|
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.filter(exchange);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlackListUrlFilter()
|
||||||
|
{
|
||||||
|
super(Config.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Config
|
||||||
|
{
|
||||||
|
private List<String> blacklistUrl;
|
||||||
|
|
||||||
|
private List<Pattern> blacklistUrlPattern = new ArrayList<>();
|
||||||
|
|
||||||
|
public boolean matchBlacklist(String url)
|
||||||
|
{
|
||||||
|
return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getBlacklistUrl()
|
||||||
|
{
|
||||||
|
return blacklistUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlacklistUrl(List<String> blacklistUrl)
|
||||||
|
{
|
||||||
|
this.blacklistUrl = blacklistUrl;
|
||||||
|
this.blacklistUrlPattern.clear();
|
||||||
|
this.blacklistUrl.forEach(url -> {
|
||||||
|
this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.grail.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 grail
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CacheRequestFilter extends AbstractGatewayFilterFactory<CacheRequestFilter.Config>
|
||||||
|
{
|
||||||
|
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<Void> 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<String> shortcutFieldOrder()
|
||||||
|
{
|
||||||
|
return Collections.singletonList("order");
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Config
|
||||||
|
{
|
||||||
|
private Integer order;
|
||||||
|
|
||||||
|
public Integer getOrder()
|
||||||
|
{
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder(Integer order)
|
||||||
|
{
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.grail.gateway.filter;
|
||||||
|
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import com.grail.gateway.config.properties.CaptchaProperties;
|
||||||
|
import com.grail.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.grail.common.core.utils.ServletUtils;
|
||||||
|
import com.grail.common.core.utils.StringUtils;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码过滤器
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
|
||||||
|
{
|
||||||
|
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<DataBuffer> body = serverHttpRequest.getBody();
|
||||||
|
AtomicReference<String> bodyRef = new AtomicReference<>();
|
||||||
|
body.subscribe(buffer -> {
|
||||||
|
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
|
||||||
|
DataBufferUtils.release(buffer);
|
||||||
|
bodyRef.set(charBuffer.toString());
|
||||||
|
});
|
||||||
|
return bodyRef.get();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package com.grail.gateway.filter;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import com.grail.gateway.config.properties.XssProperties;
|
||||||
|
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.grail.common.core.utils.StringUtils;
|
||||||
|
import com.grail.common.core.utils.html.EscapeUtil;
|
||||||
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨站脚本过滤器
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
|
||||||
|
public class XssFilter implements GlobalFilter, Ordered
|
||||||
|
{
|
||||||
|
// 跨站脚本的 xss 配置,nacos自行添加
|
||||||
|
@Autowired
|
||||||
|
private XssProperties xss;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
|
||||||
|
{
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
// GET DELETE 不过滤
|
||||||
|
HttpMethod method = request.getMethod();
|
||||||
|
if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE)
|
||||||
|
{
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
// 非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<DataBuffer> getBody()
|
||||||
|
{
|
||||||
|
Flux<DataBuffer> body = super.getBody();
|
||||||
|
return body.buffer().map(dataBuffers -> {
|
||||||
|
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
|
||||||
|
DataBuffer join = dataBufferFactory.join(dataBuffers);
|
||||||
|
byte[] content = new byte[join.readableByteCount()];
|
||||||
|
join.read(content);
|
||||||
|
DataBufferUtils.release(join);
|
||||||
|
String bodyStr = new String(content, StandardCharsets.UTF_8);
|
||||||
|
// 防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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.grail.gateway.handler;
|
||||||
|
|
||||||
|
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.grail.common.core.utils.ServletUtils;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关统一异常处理
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@Order(-1)
|
||||||
|
@Configuration
|
||||||
|
public class GatewayExceptionHandler implements ErrorWebExceptionHandler
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
|
||||||
|
{
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
|
||||||
|
if (exchange.getResponse().isCommitted())
|
||||||
|
{
|
||||||
|
return Mono.error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
String msg;
|
||||||
|
|
||||||
|
if (ex instanceof NotFoundException)
|
||||||
|
{
|
||||||
|
msg = "服务未找到";
|
||||||
|
}
|
||||||
|
else if (ex instanceof ResponseStatusException)
|
||||||
|
{
|
||||||
|
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
||||||
|
msg = responseStatusException.getMessage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg = "内部服务器错误";
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
|
||||||
|
|
||||||
|
return ServletUtils.webFluxResponseWriter(response, msg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.grail.gateway.handler;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.grail.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 grail
|
||||||
|
*/
|
||||||
|
public class SentinelFallbackHandler implements WebExceptionHandler
|
||||||
|
{
|
||||||
|
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
|
||||||
|
{
|
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> 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<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
|
||||||
|
{
|
||||||
|
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.grail.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<ResponseEntity<SecurityConfiguration>> securityConfiguration()
|
||||||
|
{
|
||||||
|
return Mono.just(new ResponseEntity<>(
|
||||||
|
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
|
||||||
|
HttpStatus.OK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/configuration/ui")
|
||||||
|
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
|
||||||
|
{
|
||||||
|
return Mono.just(new ResponseEntity<>(
|
||||||
|
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@GetMapping("")
|
||||||
|
public Mono<ResponseEntity> swaggerResources()
|
||||||
|
{
|
||||||
|
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.grail.gateway.handler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.grail.gateway.service.ValidateCodeService;
|
||||||
|
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.grail.common.core.exception.CaptchaException;
|
||||||
|
import com.grail.common.core.web.domain.AjaxResult;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码获取
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ValidateCodeHandler implements HandlerFunction<ServerResponse>
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private ValidateCodeService validateCodeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> 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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.grail.gateway.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import com.grail.common.core.exception.CaptchaException;
|
||||||
|
import com.grail.common.core.web.domain.AjaxResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码处理
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
public interface ValidateCodeService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 生成验证码
|
||||||
|
*/
|
||||||
|
public AjaxResult createCaptcha() throws IOException, CaptchaException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*/
|
||||||
|
public void checkCaptcha(String key, String value) throws CaptchaException;
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.grail.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 com.grail.gateway.config.properties.CaptchaProperties;
|
||||||
|
import com.grail.gateway.service.ValidateCodeService;
|
||||||
|
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.grail.common.core.constant.CacheConstants;
|
||||||
|
import com.grail.common.core.constant.Constants;
|
||||||
|
import com.grail.common.core.exception.CaptchaException;
|
||||||
|
import com.grail.common.core.utils.StringUtils;
|
||||||
|
import com.grail.common.core.utils.sign.Base64;
|
||||||
|
import com.grail.common.core.utils.uuid.IdUtils;
|
||||||
|
import com.grail.common.core.web.domain.AjaxResult;
|
||||||
|
import com.grail.common.redis.service.RedisService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码实现处理
|
||||||
|
*
|
||||||
|
* @author grail
|
||||||
|
*/
|
||||||
|
@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 captchaEnabled = captchaProperties.getEnabled();
|
||||||
|
ajax.put("captchaEnabled", captchaEnabled);
|
||||||
|
if (!captchaEnabled)
|
||||||
|
{
|
||||||
|
return ajax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存验证码信息
|
||||||
|
String uuid = IdUtils.simpleUUID();
|
||||||
|
String verifyKey = CacheConstants.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.HOURS);
|
||||||
|
// 转换流信息写出
|
||||||
|
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 = CacheConstants.CAPTCHA_CODE_KEY + uuid;
|
||||||
|
String captcha = redisService.getCacheObject(verifyKey);
|
||||||
|
redisService.deleteObject(verifyKey);
|
||||||
|
|
||||||
|
if (!code.equalsIgnoreCase(captcha))
|
||||||
|
{
|
||||||
|
throw new CaptchaException("验证码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
Spring Boot Version: ${spring-boot.version}
|
||||||
|
Spring Application Name: ${spring.application.name}
|
||||||
|
_ _
|
||||||
|
(_) | |
|
||||||
|
_ __ _ _ ___ _ _ _ ______ __ _ __ _ | |_ ___ __ __ __ _ _ _
|
||||||
|
| '__|| | | | / _ \ | | | || ||______| / _` | / _` || __| / _ \\ \ /\ / / / _` || | | |
|
||||||
|
| | | |_| || (_) || |_| || | | (_| || (_| || |_ | __/ \ V V / | (_| || |_| |
|
||||||
|
|_| \__,_| \___/ \__, ||_| \__, | \__,_| \__| \___| \_/\_/ \__,_| \__, |
|
||||||
|
__/ | __/ | __/ |
|
||||||
|
|___/ |___/ |___/
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Tomcat
|
||||||
|
server:
|
||||||
|
port: 8078
|
||||||
|
|
||||||
|
# Spring
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
# 应用名称
|
||||||
|
name: grail-gateway
|
||||||
|
profiles:
|
||||||
|
# 环境配置
|
||||||
|
active: dev
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
# 服务注册地址
|
||||||
|
server-addr: 10.100.1.3:8848
|
||||||
|
config:
|
||||||
|
# 配置中心地址
|
||||||
|
server-addr: 10.100.1.3:8848
|
||||||
|
# 配置文件格式
|
||||||
|
file-extension: yml
|
||||||
|
# 共享配置
|
||||||
|
shared-configs:
|
||||||
|
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
sentinel:
|
||||||
|
# 取消控制台懒加载
|
||||||
|
eager: true
|
||||||
|
transport:
|
||||||
|
# 控制台地址
|
||||||
|
dashboard: 127.0.0.1:8718
|
||||||
|
# nacos配置持久化
|
||||||
|
datasource:
|
||||||
|
ds1:
|
||||||
|
nacos:
|
||||||
|
server-addr: 10.100.1.3:8848
|
||||||
|
dataId: sentinel-grail-gateway
|
||||||
|
groupId: DEFAULT_GROUP
|
||||||
|
data-type: json
|
||||||
|
rule-type: flow
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||||
|
<!-- 日志存放路径 -->
|
||||||
|
<property name="log.path" value="./logs/grail-gateway" />
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||||
|
|
||||||
|
<!-- 控制台输出 -->
|
||||||
|
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统日志输出 -->
|
||||||
|
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/info.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 过滤的级别 -->
|
||||||
|
<level>INFO</level>
|
||||||
|
<!-- 匹配时的操作:接收(记录) -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/error.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 过滤的级别 -->
|
||||||
|
<level>ERROR</level>
|
||||||
|
<!-- 匹配时的操作:接收(记录) -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统模块日志级别控制 -->
|
||||||
|
<logger name="com.grail" level="info" />
|
||||||
|
<!-- Spring日志级别控制 -->
|
||||||
|
<logger name="org.springframework" level="warn" />
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="console" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<!--系统操作日志-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="file_info" />
|
||||||
|
<appender-ref ref="file_error" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
Loading…
Reference in New Issue