初始化

master
bai 2024-08-07 23:50:32 +08:00
commit 9c4d30cdd0
78 changed files with 2546 additions and 0 deletions

8
.idea/.gitignore vendored 100644
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/compiler.xml 100644
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="cloud-gateway" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,14 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AliAccessStaticViaInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliArrayNamingShouldHaveBracket" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliControlFlowStatementWithoutBraces" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliDeprecation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliEqualsAvoidNull" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliLongLiteralsEndingWithLowercaseL" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliMissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliWrapperTypeEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MapOrSetKeyShouldOverrideHashCodeEquals" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="public" />
<option name="name" value="aliyun nexus" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://8.153.39.174:8081/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

12
.idea/misc.xml 100644
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17 (2)" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml 100644
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

16
Dockerfile 100644
View File

@ -0,0 +1,16 @@
# home
# app.jar ---启动jar包
# logs 日志文件
#指定构建镜像的起始镜像
FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/dragonwell:17.0.4.0.4.8-standard-ga-8.6
#执行一些必备的条件
#定义时区参数
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
#挂载工作目录
VOLUME ["/home/deploy/file/logs/cloud-gateway"]
#拷贝执行jar包文件
COPY ./target/cloud-gateway.jar /home/app.jar
ENTRYPOINT ["java", "-jar"]
CMD ["/home/app.jar"]

View File

@ -0,0 +1 @@
01:07:47.552 [com.alibaba.nacos.client.naming.grpc.redo.0] ERROR c.a.n.c.r.client - [printIfErrorEnabled,102] - Send request fail, request = InstanceRequest{headers={accessToken=AUTH_DISABLED, app=unknown}, requestId='null'}, retryTimes = 0, errorMessage = java.lang.InterruptedException

View File

@ -0,0 +1,118 @@
00:34:47.246 [main] INFO c.m.g.CloudGatewayApplication - [logStartupProfileInfo,660] - The following 1 profile is active: "dev"
00:34:56.116 [main] INFO c.a.c.s.g.s.SentinelSCGAutoConfiguration - [sentinelGatewayFilter,143] - [Sentinel SpringCloudGateway] register SentinelGatewayFilter with order: -2147483648
00:34:57.132 [main] INFO c.a.c.s.g.s.SentinelSCGAutoConfiguration - [sentinelGatewayBlockExceptionHandler,133] - [Sentinel SpringCloudGateway] register SentinelGatewayBlockExceptionHandler
00:34:58.339 [main] INFO c.a.n.common.labels - [getLabels,48] - DefaultLabelsCollectorManager get labels.....
00:34:58.340 [main] INFO c.a.n.common.labels - [getLabels,62] - Process LabelsCollector with [name:defaultNacosLabelsCollector]
00:34:58.341 [main] INFO c.a.n.common.labels - [collectLabels,62] - default nacos collect properties raw labels: null
00:34:58.341 [main] INFO c.a.n.common.labels - [collectLabels,69] - default nacos collect properties labels: {}
00:34:58.341 [main] INFO c.a.n.common.labels - [collectLabels,72] - default nacos collect jvm raw labels: null
00:34:58.343 [main] INFO c.a.n.common.labels - [collectLabels,78] - default nacos collect jvm labels: {}
00:34:58.343 [main] INFO c.a.n.common.labels - [collectLabels,81] - default nacos collect env raw labels: null
00:34:58.344 [main] INFO c.a.n.common.labels - [collectLabels,89] - default nacos collect env labels: {}
00:34:58.344 [main] INFO c.a.n.common.labels - [getLabels,50] - DefaultLabelsCollectorManager get labels finished,labels :{}
00:34:58.344 [main] INFO c.a.n.p.a.s.c.ClientAuthPluginManager - [init,56] - [ClientAuthPluginManager] Load ClientAuthService com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl success.
00:34:58.344 [main] INFO c.a.n.p.a.s.c.ClientAuthPluginManager - [init,56] - [ClientAuthPluginManager] Load ClientAuthService com.alibaba.nacos.client.auth.ram.RamClientAuthServiceImpl success.
00:34:58.350 [main] INFO c.a.n.c.c.i.CacheData - [initNotifyWarnTimeout,72] - config listener notify warn timeout millis use default 60000 millis
00:34:58.350 [main] INFO c.a.n.c.c.i.CacheData - [<clinit>,99] - nacos.cache.data.init.snapshot = true
00:34:58.366 [main] INFO c.a.n.c.c.i.ClientWorker - [addCacheDataIfAbsent,416] - [fixed-cloud-2112-47.102.201.146_8848] [subscribe] sentinel-cloud-gateway+DEFAULT_GROUP+cloud-2112
00:34:58.386 [main] INFO c.a.n.c.c.i.CacheData - [addListener,236] - [fixed-cloud-2112-47.102.201.146_8848] [add-listener] ok, tenant=cloud-2112, dataId=sentinel-cloud-gateway, group=DEFAULT_GROUP, cnt=1
00:34:58.387 [main] INFO c.a.n.c.r.client - [lambda$createClient$0,118] - [RpcClientFactory] create a new rpc client of c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0
00:34:58.388 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda$525/0x0000016648364010
00:34:58.388 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda$526/0x0000016648364430
00:34:58.388 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Registry connection listener to current client:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$1
00:34:58.389 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] RpcClient init, ServerListFactory = com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$2
00:34:58.390 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Try to connect to server on start up, server: {serverIp = '47.102.201.146', server main port = 8848}
00:34:58.390 [main] INFO c.a.n.c.r.c.g.GrpcClient - [createNewManagedChannel,210] - grpc client connection server:47.102.201.146 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
00:34:58.574 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Success to connect to server [47.102.201.146:8848] on start up, connectionId = 1722357298084_101.82.127.1_54059
00:34:58.574 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
00:34:58.574 [com.alibaba.nacos.client.remote.worker.0] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Notify connected event to listeners.
00:34:58.576 [com.alibaba.nacos.client.remote.worker.0] INFO c.a.n.c.c.i.ClientWorker - [onConnected,713] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Connected,notify listen context...
00:34:58.582 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [c7a4b4b9-a5e7-49fd-8803-f4cc0284a87e_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda$546/0x00000166484bbe60
00:35:00.069 [main] INFO c.a.n.client.naming - [initNamespaceForNaming,62] - initializer namespace from ans.namespace attribute : null
00:35:00.070 [main] INFO c.a.n.client.naming - [lambda$initNamespaceForNaming$0,66] - initializer namespace from ALIBABA_ALIWARE_NAMESPACE attribute :null
00:35:00.071 [main] INFO c.a.n.client.naming - [lambda$initNamespaceForNaming$1,73] - initializer namespace from namespace attribute :null
00:35:00.084 [main] INFO c.a.n.client.naming - [<init>,74] - FailoverDataSource type is class com.alibaba.nacos.client.naming.backups.datasource.DiskFailoverDataSource
00:35:00.094 [main] INFO c.a.n.p.a.s.c.ClientAuthPluginManager - [init,56] - [ClientAuthPluginManager] Load ClientAuthService com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl success.
00:35:00.094 [main] INFO c.a.n.p.a.s.c.ClientAuthPluginManager - [init,56] - [ClientAuthPluginManager] Load ClientAuthService com.alibaba.nacos.client.auth.ram.RamClientAuthServiceImpl success.
00:35:00.642 [main] INFO c.a.n.c.r.client - [lambda$createClient$0,118] - [RpcClientFactory] create a new rpc client of a260ba7d-96a6-475b-bfa9-026e0909070e
00:35:00.645 [main] INFO c.a.n.client.naming - [<init>,109] - Create naming rpc client for uuid->a260ba7d-96a6-475b-bfa9-026e0909070e
00:35:00.646 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] RpcClient init, ServerListFactory = com.alibaba.nacos.client.naming.core.ServerListManager
00:35:00.646 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Registry connection listener to current client:com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService
00:35:00.648 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Register server push request handler:com.alibaba.nacos.client.naming.remote.gprc.NamingPushRequestHandler
00:35:00.649 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Try to connect to server on start up, server: {serverIp = '47.102.201.146', server main port = 8848}
00:35:00.649 [main] INFO c.a.n.c.r.c.g.GrpcClient - [createNewManagedChannel,210] - grpc client connection server:47.102.201.146 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
00:35:00.813 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Success to connect to server [47.102.201.146:8848] on start up, connectionId = 1722357300321_101.82.127.1_54061
00:35:00.813 [com.alibaba.nacos.client.remote.worker.0] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Notify connected event to listeners.
00:35:00.813 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
00:35:00.813 [com.alibaba.nacos.client.remote.worker.0] INFO c.a.n.client.naming - [onConnected,90] - Grpc connection connect
00:35:00.815 [main] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda$546/0x00000166484bbe60
00:35:00.817 [main] INFO c.a.n.client.naming - [registerService,133] - [REGISTER-SERVICE] cloud-2112 registering service cloud-gateway with instance Instance{instanceId='null', ip='192.168.37.230', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='null', metadata={IPv6=null, preserved.register.source=SPRING_CLOUD}}
00:35:00.881 [main] INFO c.a.c.n.r.NacosServiceRegistry - [register,76] - nacos registry, DEFAULT_GROUP cloud-gateway 192.168.37.230:8080 register finished
00:35:01.051 [boundedElastic-4] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-gateway, group:DEFAULT_GROUP, clusters:
00:35:01.051 [boundedElastic-3] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-auth, group:DEFAULT_GROUP, clusters:
00:35:01.051 [boundedElastic-4] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-gateway, group:DEFAULT_GROUP, cluster:
00:35:01.051 [boundedElastic-3] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-auth, group:DEFAULT_GROUP, cluster:
00:35:01.052 [boundedElastic-1] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-auth, group:DEFAULT_GROUP, clusters:
00:35:01.054 [boundedElastic-1] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-auth, group:DEFAULT_GROUP, cluster:
00:35:01.055 [boundedElastic-5] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-gateway, group:DEFAULT_GROUP, clusters:
00:35:01.055 [boundedElastic-5] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-gateway, group:DEFAULT_GROUP, cluster:
00:35:01.107 [boundedElastic-3] INFO c.a.n.client.naming - [isChangedServiceInfo,169] - init new ips(1) service: DEFAULT_GROUP@@cloud-auth -> [{"instanceId":"192.168.37.230#9500#DEFAULT#DEFAULT_GROUP@@cloud-auth","ip":"192.168.37.230","port":9500,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-auth","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:35:01.107 [boundedElastic-4] INFO c.a.n.client.naming - [isChangedServiceInfo,169] - init new ips(1) service: DEFAULT_GROUP@@cloud-gateway -> [{"instanceId":"192.168.37.230#8080#DEFAULT#DEFAULT_GROUP@@cloud-gateway","ip":"192.168.37.230","port":8080,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-gateway","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:35:01.110 [boundedElastic-3] INFO c.a.n.client.naming - [processServiceInfo,144] - current ips:(1) service: DEFAULT_GROUP@@cloud-auth -> [{"instanceId":"192.168.37.230#9500#DEFAULT#DEFAULT_GROUP@@cloud-auth","ip":"192.168.37.230","port":9500,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-auth","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:35:01.110 [boundedElastic-4] INFO c.a.n.client.naming - [processServiceInfo,144] - current ips:(1) service: DEFAULT_GROUP@@cloud-gateway -> [{"instanceId":"192.168.37.230#8080#DEFAULT#DEFAULT_GROUP@@cloud-gateway","ip":"192.168.37.230","port":8080,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-gateway","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:35:01.282 [main] INFO c.a.c.n.d.NacosDiscoveryHeartBeatPublisher - [start,66] - Start nacos heartBeat task scheduler.
00:35:01.393 [main] INFO c.m.g.CloudGatewayApplication - [logStarted,56] - Started CloudGatewayApplication in 20.92 seconds (process running for 23.179)
00:35:01.395 [nacos-grpc-client-executor-47.102.201.146-1] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Receive server push request, request = NotifySubscriberRequest, requestId = 35
00:35:01.396 [nacos-grpc-client-executor-47.102.201.146-1] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Ack server push request, request = NotifySubscriberRequest, requestId = 35
00:35:01.402 [main] INFO c.a.n.c.c.i.ClientWorker - [addCacheDataIfAbsent,416] - [fixed-cloud-2112-47.102.201.146_8848] [subscribe] cloud-gateway+DEFAULT_GROUP+cloud-2112
00:35:01.402 [main] INFO c.a.n.c.c.i.CacheData - [addListener,236] - [fixed-cloud-2112-47.102.201.146_8848] [add-listener] ok, tenant=cloud-2112, dataId=cloud-gateway, group=DEFAULT_GROUP, cnt=1
00:35:01.403 [main] INFO c.a.c.n.r.NacosContextRefresher - [registerNacosListener,131] - [Nacos Config] Listening config: dataId=cloud-gateway, group=DEFAULT_GROUP
00:35:01.404 [main] INFO c.a.n.c.c.i.ClientWorker - [addCacheDataIfAbsent,416] - [fixed-cloud-2112-47.102.201.146_8848] [subscribe] cloud-gateway.yml+DEFAULT_GROUP+cloud-2112
00:35:01.406 [main] INFO c.a.n.c.c.i.CacheData - [addListener,236] - [fixed-cloud-2112-47.102.201.146_8848] [add-listener] ok, tenant=cloud-2112, dataId=cloud-gateway.yml, group=DEFAULT_GROUP, cnt=1
00:35:01.406 [main] INFO c.a.c.n.r.NacosContextRefresher - [registerNacosListener,131] - [Nacos Config] Listening config: dataId=cloud-gateway.yml, group=DEFAULT_GROUP
00:35:01.423 [main] INFO c.a.n.c.c.i.ClientWorker - [addCacheDataIfAbsent,416] - [fixed-cloud-2112-47.102.201.146_8848] [subscribe] cloud-gateway-dev.yml+DEFAULT_GROUP+cloud-2112
00:35:01.424 [main] INFO c.a.n.c.c.i.CacheData - [addListener,236] - [fixed-cloud-2112-47.102.201.146_8848] [add-listener] ok, tenant=cloud-2112, dataId=cloud-gateway-dev.yml, group=DEFAULT_GROUP, cnt=1
00:35:01.424 [main] INFO c.a.c.n.r.NacosContextRefresher - [registerNacosListener,131] - [Nacos Config] Listening config: dataId=cloud-gateway-dev.yml, group=DEFAULT_GROUP
00:35:01.694 [nacos-grpc-client-executor-47.102.201.146-11] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Receive server push request, request = NotifySubscriberRequest, requestId = 36
00:35:01.695 [nacos-grpc-client-executor-47.102.201.146-11] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Ack server push request, request = NotifySubscriberRequest, requestId = 36
00:57:33.678 [nacos-grpc-client-executor-47.102.201.146-556] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Receive server push request, request = NotifySubscriberRequest, requestId = 37
00:57:33.681 [nacos-grpc-client-executor-47.102.201.146-556] INFO c.a.n.client.naming - [isChangedServiceInfo,219] - new ips(1) service: DEFAULT_GROUP@@cloud-gateway -> [{"instanceId":"172.16.16.1#8080#DEFAULT#DEFAULT_GROUP@@cloud-gateway","ip":"172.16.16.1","port":8080,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-gateway","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:57:33.687 [nacos-grpc-client-executor-47.102.201.146-556] INFO c.a.n.client.naming - [processServiceInfo,144] - current ips:(2) service: DEFAULT_GROUP@@cloud-gateway -> [{"instanceId":"192.168.37.230#8080#DEFAULT#DEFAULT_GROUP@@cloud-gateway","ip":"192.168.37.230","port":8080,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-gateway","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000},{"instanceId":"172.16.16.1#8080#DEFAULT#DEFAULT_GROUP@@cloud-gateway","ip":"172.16.16.1","port":8080,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-gateway","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
00:57:33.701 [nacos-grpc-client-executor-47.102.201.146-556] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Ack server push request, request = NotifySubscriberRequest, requestId = 37
01:06:15.413 [boundedElastic-17] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-file, group:DEFAULT_GROUP, clusters:
01:06:15.414 [boundedElastic-17] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-file, group:DEFAULT_GROUP, cluster:
01:06:15.422 [boundedElastic-20] INFO c.a.n.client.naming - [subscribe,164] - [SUBSCRIBE-SERVICE] service:cloud-file, group:DEFAULT_GROUP, clusters:
01:06:15.422 [boundedElastic-20] INFO c.a.n.client.naming - [subscribe,377] - [GRPC-SUBSCRIBE] service:cloud-file, group:DEFAULT_GROUP, cluster:
01:06:15.454 [boundedElastic-17] INFO c.a.n.client.naming - [isChangedServiceInfo,169] - init new ips(1) service: DEFAULT_GROUP@@cloud-file -> [{"instanceId":"172.16.16.1#9300#DEFAULT#DEFAULT_GROUP@@cloud-file","ip":"172.16.16.1","port":9300,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-file","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
01:06:15.456 [boundedElastic-17] INFO c.a.n.client.naming - [processServiceInfo,144] - current ips:(1) service: DEFAULT_GROUP@@cloud-file -> [{"instanceId":"172.16.16.1#9300#DEFAULT#DEFAULT_GROUP@@cloud-file","ip":"172.16.16.1","port":9300,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-file","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
01:06:16.027 [nacos-grpc-client-executor-47.102.201.146-761] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Receive server push request, request = NotifySubscriberRequest, requestId = 40
01:06:16.028 [nacos-grpc-client-executor-47.102.201.146-761] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Ack server push request, request = NotifySubscriberRequest, requestId = 40
01:07:41.749 [nacos-grpc-client-executor-47.102.201.146-789] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Receive server push request, request = NotifySubscriberRequest, requestId = 42
01:07:41.750 [nacos-grpc-client-executor-47.102.201.146-789] INFO c.a.n.client.naming - [isChangedServiceInfo,225] - removed ips(1) service: DEFAULT_GROUP@@cloud-auth -> [{"instanceId":"192.168.37.230#9500#DEFAULT#DEFAULT_GROUP@@cloud-auth","ip":"192.168.37.230","port":9500,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@cloud-auth","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000,"ipDeleteTimeout":30000}]
01:07:41.750 [nacos-grpc-client-executor-47.102.201.146-789] INFO c.a.n.client.naming - [processServiceInfo,144] - current ips:(0) service: DEFAULT_GROUP@@cloud-auth -> []
01:07:41.764 [nacos-grpc-client-executor-47.102.201.146-789] INFO c.a.n.c.r.client - [printIfInfoEnabled,63] - [a260ba7d-96a6-475b-bfa9-026e0909070e] Ack server push request, request = NotifySubscriberRequest, requestId = 42
01:07:47.500 [SpringApplicationShutdownHook] INFO c.a.c.n.r.NacosServiceRegistry - [deregister,95] - De-registering from Nacos Server now...
01:07:47.501 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [deregisterService,272] - [DEREGISTER-SERVICE] cloud-2112 deregistering service cloud-gateway with instance: Instance{instanceId='null', ip='192.168.37.230', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='null', metadata={}}
01:07:47.537 [com.alibaba.nacos.client.naming.grpc.redo.0] INFO c.a.n.client.naming - [redoForInstance,73] - Redo instance operation UNREGISTER for DEFAULT_GROUP@@cloud-gateway
01:07:47.543 [SpringApplicationShutdownHook] INFO c.a.c.n.r.NacosServiceRegistry - [deregister,115] - De-registration finished.
01:07:47.545 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,254] - com.alibaba.nacos.client.naming.cache.ServiceInfoHolder do shutdown begin
01:07:47.545 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,180] - com.alibaba.nacos.client.naming.backups.FailoverReactor do shutdown begin
01:07:47.545 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,182] - com.alibaba.nacos.client.naming.backups.FailoverReactor do shutdown stop
01:07:47.546 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,256] - com.alibaba.nacos.client.naming.cache.ServiceInfoHolder do shutdown stop
01:07:47.546 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,204] - com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate do shutdown begin
01:07:47.547 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,147] - com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService do shutdown begin
01:07:47.547 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,149] - com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService do shutdown stop
01:07:47.547 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,218] - com.alibaba.nacos.client.naming.core.ServerListManager do shutdown begin
01:07:47.549 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,223] - com.alibaba.nacos.client.naming.core.ServerListManager do shutdown stop
01:07:47.549 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,468] - com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy do shutdown begin
01:07:47.550 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,470] - com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy do shutdown stop
01:07:47.550 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,487] - Shutdown naming grpc client proxy for uuid->a260ba7d-96a6-475b-bfa9-026e0909070e
01:07:47.550 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,331] - Shutdown grpc redo service executor java.util.concurrent.ScheduledThreadPoolExecutor@5c7f2b1a[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 653]
01:07:47.550 [SpringApplicationShutdownHook] INFO c.a.n.c.r.client - [shutdown,425] - Shutdown rpc client, set status to shutdown
01:07:47.552 [SpringApplicationShutdownHook] INFO c.a.n.c.r.client - [shutdown,427] - Shutdown client event executor java.util.concurrent.ScheduledThreadPoolExecutor@2ac1fd97[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
01:07:47.552 [SpringApplicationShutdownHook] INFO c.a.n.c.r.client - [closeConnection,584] - Close current connection 1722357300321_101.82.127.1_54061
01:07:47.561 [SpringApplicationShutdownHook] INFO c.a.n.c.r.c.g.GrpcClient - [shutdown,187] - Shutdown grpc executor java.util.concurrent.ThreadPoolExecutor@378926d5[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 807]
01:07:47.562 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutDownAndRemove,497] - shutdown and remove naming rpc client for uuid ->a260ba7d-96a6-475b-bfa9-026e0909070e
01:07:47.562 [SpringApplicationShutdownHook] INFO c.a.n.c.a.r.i.CredentialWatcher - [stop,107] - [null] CredentialWatcher is stopped
01:07:47.563 [SpringApplicationShutdownHook] INFO c.a.n.c.a.r.i.CredentialService - [free,91] - [null] CredentialService is freed
01:07:47.563 [SpringApplicationShutdownHook] INFO c.a.n.client.naming - [shutdown,211] - com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate do shutdown stop

114
pom.xml 100644
View File

@ -0,0 +1,114 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.muyu</groupId>
<artifactId>cloud-server</artifactId>
<version>3.6.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway</artifactId>
<version>1.0.0</version>
<description>
cloud-gateway网关模块
</description>
<dependencies>
<!-- SpringCloud 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>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<!-- MuYu Common Redis-->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>cloud-common-redis</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.5.0</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>
<!-- 加入maven deploy插件当在deploy时忽略些model-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,17 @@
package com.muyu.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
*
*
* @author muyu
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class CloudGatewayApplication {
public static void main (String[] args) {
SpringApplication.run(CloudGatewayApplication.class, args);
}
}

View File

@ -0,0 +1,82 @@
package com.muyu.gateway.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
import static com.google.code.kaptcha.Constants.*;
/**
*
*
* @author muyu
*/
@Configuration
public class CaptchaConfig {
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean () {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yesno
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 我们可以自己设置yesno
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.muyu.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;
}
}

View File

@ -0,0 +1,21 @@
package com.muyu.gateway.config;
import com.muyu.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 muyu
*/
@Configuration
public class GatewayConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler () {
return new SentinelFallbackHandler();
}
}

View File

@ -0,0 +1,61 @@
package com.muyu.gateway.config;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.util.Random;
/**
*
*
* @author muyu
*/
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();
}
}

View File

@ -0,0 +1,29 @@
package com.muyu.gateway.config;
import com.muyu.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 muyu
*/
@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);
}
}

View File

@ -0,0 +1,41 @@
package com.muyu.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 muyu
*/
@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;
}
}

View File

@ -0,0 +1,31 @@
package com.muyu.gateway.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author muyu
*/
@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;
}
}

View File

@ -0,0 +1,44 @@
package com.muyu.gateway.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* XSS
*
* @author muyu
*/
@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;
}
}

View File

@ -0,0 +1,226 @@
package com.muyu.gateway.filter;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.muyu.common.core.constant.SecurityConstants;
import com.muyu.gateway.model.AccessLog;
import com.muyu.gateway.utils.WebFrameworkUtils;
import lombok.extern.log4j.Log4j2;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
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.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
/**
* 访
* <p>
* <p>
* TODO 访 https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging
*/
@Log4j2
@Component
public class AccessLogFilter implements GlobalFilter, Ordered {
private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
/**
*
*
* @param gatewayLog
*/
private void writeAccessLog(AccessLog gatewayLog) {
log.info("[网关日志:{}]", gatewayLog.toString());
}
@Override
public int getOrder() {
return -99;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 将 Request 中可以直接获取到的参数,设置到网关日志
ServerHttpRequest request = exchange.getRequest();
// TODO traceId
AccessLog accessLog = AccessLog.builder()
.userId(request.getHeaders().getFirst(SecurityConstants.DETAILS_USER_ID))
.route(WebFrameworkUtils.getGatewayRoute(exchange))
.schema(request.getURI().getScheme())
.requestMethod(request.getMethod().name())
.requestUrl(request.getURI().getRawPath())
.queryParams(request.getQueryParams())
.requestHeaders(request.getHeaders())
.startTime(LocalDateTime.now())
.userIp(WebFrameworkUtils.getClientIP(exchange))
.build();
// 继续 filter 过滤
MediaType mediaType = request.getHeaders().getContentType();
return MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)
?
filterWithRequestBody(exchange, chain, accessLog)
:
filterWithoutRequestBody(exchange, chain, accessLog);
}
private Mono<Void> filterWithoutRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog accessLog) {
// 包装 Response用于记录 Response Body
ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);
return chain.filter(exchange.mutate().response(decoratedResponse).build())
.then(Mono.fromRunnable(() -> writeAccessLog(accessLog))); // 打印日志
}
/**
* {@link ModifyRequestBodyGatewayFilterFactory}
* <p>
* 使 modifiedBody Request Body
*/
private Mono<Void> filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog gatewayLog) {
// 设置 Request Body 读取时,设置到网关日志
ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
gatewayLog.setRequestBody(body);
return Mono.just(body);
});
// 创建 BodyInserter 对象
BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
// 创建 CachedBodyOutputMessage 对象
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// the new content type will be computed by bodyInserter
// and then set in the request decorator
headers.remove(HttpHeaders.CONTENT_LENGTH); // 移除
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
// 通过 BodyInserter 将 Request Body 写入到 CachedBodyOutputMessage 中
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
// 包装 Request用于缓存 Request Body
ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);
// 包装 Response用于记录 Response Body
ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);
// 记录普通的
return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())
.then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); // 打印日志
}));
}
/**
*
* DataBufferFactory
*/
private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, AccessLog gatewayLog) {
ServerHttpResponse response = exchange.getResponse();
return new ServerHttpResponseDecorator(response) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
DataBufferFactory bufferFactory = response.bufferFactory();
// 计算执行时间
gatewayLog.setEndTime(LocalDateTime.now());
gatewayLog.setDuration((int) (LocalDateTimeUtil.between(gatewayLog.getStartTime(),
gatewayLog.getEndTime()).toMillis()));
// 设置其它字段
// gatewayLog.setUserId(SecurityFrameworkUtils.getLoginUserId(exchange));
gatewayLog.setResponseHeaders(response.getHeaders());
gatewayLog.setHttpStatus(response.getStatusCode());
// 获取响应类型,如果是 json 就打印
String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
if (StringUtils.isNotBlank(originalResponseContentType)
&& originalResponseContentType.contains("application/json")) {
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
// 设置 response body 到网关日志
byte[] content = readContent(dataBuffers);
String responseResult = new String(content, StandardCharsets.UTF_8);
gatewayLog.setResponseBody(responseResult);
// 响应
return bufferFactory.wrap(content);
}));
}
}
// if body is not a flux. never got there.
return super.writeWith(body);
}
};
}
// ========== 参考 ModifyRequestBodyGatewayFilterFactory 中的方法 ==========
/**
* headersbody
*
* @param exchange
* @param headers
* @param outputMessage body
* @return
*/
private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
return new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
}
// ========== 参考 ModifyResponseBodyGatewayFilterFactory 中的方法 ==========
private byte[] readContent(List<? extends DataBuffer> dataBuffers) {
// 合并多个流集合,解决返回体分段传输
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
// 释放掉内存
DataBufferUtils.release(join);
return content;
}
}

View File

@ -0,0 +1,120 @@
package com.muyu.gateway.filter;
import com.muyu.common.core.constant.CacheConstants;
import com.muyu.common.core.constant.HttpStatus;
import com.muyu.common.core.constant.SecurityConstants;
import com.muyu.common.core.constant.TokenConstants;
import com.muyu.common.core.utils.JwtUtils;
import com.muyu.common.core.utils.ServletUtils;
import com.muyu.common.core.utils.StringUtils;
import com.muyu.common.redis.service.RedisService;
import com.muyu.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
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 reactor.core.publisher.Mono;
/**
*
*
* @author muyu
*/
@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;
}
}

View File

@ -0,0 +1,58 @@
package com.muyu.gateway.filter;
import com.muyu.common.core.utils.ServletUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
*
*
* @author muyu
*/
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> {
public BlackListUrlFilter () {
super(Config.class);
}
@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 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));
});
}
}
}

View File

@ -0,0 +1,75 @@
package com.muyu.gateway.filter;
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;
import java.util.Collections;
import java.util.List;
/**
* body
*
* @author muyu
*/
@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);
}
@Override
public List<String> shortcutFieldOrder () {
return Collections.singletonList("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());
});
}
}
static class Config {
private Integer order;
public Integer getOrder () {
return order;
}
public void setOrder (Integer order) {
this.order = order;
}
}
}

View File

@ -0,0 +1,69 @@
package com.muyu.gateway.filter;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.muyu.common.core.utils.ServletUtils;
import com.muyu.common.core.utils.StringUtils;
import com.muyu.gateway.config.properties.CaptchaProperties;
import com.muyu.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 reactor.core.publisher.Flux;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
/**
*
*
* @author muyu
*/
@Component
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> {
private final static String[] VALIDATE_URL = new String[]{"/auth/login", "/auth/register"};
private static final String CODE = "code";
private static final String UUID = "uuid";
@Autowired
private ValidateCodeService validateCodeService;
@Autowired
private CaptchaProperties captchaProperties;
@Override
public GatewayFilter apply (Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 非登录/注册请求或验证码关闭,不处理
if (!StringUtils.equalsAnyIgnoreCase(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();
}
}

View File

@ -0,0 +1,114 @@
package com.muyu.gateway.filter;
import com.muyu.common.core.utils.StringUtils;
import com.muyu.common.core.utils.html.EscapeUtil;
import com.muyu.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
*
*
* @author muyu
*/
@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();
// xss开关未开启 或 通过nacos关闭不过滤
if (!xss.getEnabled()) {
return chain.filter(exchange);
}
// 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(StandardCharsets.UTF_8);
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;
}
}

View File

@ -0,0 +1,48 @@
package com.muyu.gateway.handler;
import com.muyu.common.core.utils.ServletUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
*
*
* @author muyu
*/
@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);
}
}

View File

@ -0,0 +1,35 @@
package com.muyu.gateway.handler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.muyu.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 muyu
*/
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);
}
}

View File

@ -0,0 +1,37 @@
package com.muyu.gateway.handler;
import com.muyu.common.core.exception.CaptchaException;
import com.muyu.common.core.domain.Result;
import com.muyu.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 reactor.core.publisher.Mono;
import java.io.IOException;
/**
*
*
* @author muyu
*/
@Component
public class ValidateCodeHandler implements HandlerFunction<ServerResponse> {
@Autowired
private ValidateCodeService validateCodeService;
@Override
public Mono<ServerResponse> handle (ServerRequest serverRequest) {
Result ajax;
try {
ajax = validateCodeService.createCaptcha();
} catch (CaptchaException | IOException e) {
return Mono.error(e);
}
return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));
}
}

View File

@ -0,0 +1,120 @@
package com.muyu.gateway.model;
import com.muyu.common.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.http.HttpStatusCode;
import org.springframework.util.MultiValueMap;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 访
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccessLog {
/**
*
*/
private String traceId;
/**
*
*/
private String userId;
/**
*
*
* ApiAccessLogCreateReqDTO applicationName
*/
private Route route;
/**
*
*/
private String schema;
/**
*
*/
private String requestMethod;
/**
* 访
*/
private String requestUrl;
/**
*
*/
private MultiValueMap<String, String> queryParams;
/**
*
*/
private String requestBody;
/**
*
*/
private MultiValueMap<String, String> requestHeaders;
/**
* IP
*/
private String userIp;
/**
*
*
* ApiAccessLogCreateReqDTO resultCode + resultMsg
*/
private String responseBody;
/**
*
*/
private MultiValueMap<String, String> responseHeaders;
/**
*
*/
private HttpStatusCode httpStatus;
/**
*
*/
private LocalDateTime startTime;
/**
*
*/
private LocalDateTime endTime;
/**
*
*/
private Integer duration;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("请求简略信息",
StringUtils.format("[userId:[{}]-userIp:[{}]-traceId:[{}]] ---结果--- {{}-{}}:{} ---> {}",userId, userIp, traceId, schema, requestMethod, requestUrl,httpStatus)
)
.append("路由", route)
.append("查询参数", queryParams)
.append("请求体", requestBody)
.append("请求头", requestHeaders)
.append("响应体", responseBody)
.append("响应头", responseHeaders)
.append("耗时/时间",
StringUtils.format(
"{}MS-{}-{}",
duration,
startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
)
.toString();
}
}

View File

@ -0,0 +1,25 @@
package com.muyu.gateway.model.resp;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author DongZl
* @description:
* @Date 2023-11-12 03:36
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CaptchaCodeResp {
private boolean captchaEnabled;
private String uuid;
private String img;
}

View File

@ -0,0 +1,23 @@
package com.muyu.gateway.service;
import com.muyu.common.core.exception.CaptchaException;
import com.muyu.common.core.domain.Result;
import java.io.IOException;
/**
*
*
* @author muyu
*/
public interface ValidateCodeService {
/**
*
*/
public Result createCaptcha () throws IOException, CaptchaException;
/**
*
*/
public void checkCaptcha (String key, String value) throws CaptchaException;
}

View File

@ -0,0 +1,109 @@
package com.muyu.gateway.service.impl;
import com.google.code.kaptcha.Producer;
import com.muyu.common.core.constant.CacheConstants;
import com.muyu.common.core.constant.Constants;
import com.muyu.common.core.exception.CaptchaException;
import com.muyu.common.core.utils.StringUtils;
import com.muyu.common.core.utils.sign.Base64;
import com.muyu.common.core.utils.uuid.IdUtils;
import com.muyu.common.core.domain.Result;
import com.muyu.common.redis.service.RedisService;
import com.muyu.gateway.config.properties.CaptchaProperties;
import com.muyu.gateway.model.resp.CaptchaCodeResp;
import com.muyu.gateway.service.ValidateCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
*
*
* @author muyu
*/
@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 Result createCaptcha () throws IOException, CaptchaException {
boolean captchaEnabled = captchaProperties.getEnabled();
CaptchaCodeResp.CaptchaCodeRespBuilder respBuilder = CaptchaCodeResp.builder()
.captchaEnabled(captchaEnabled);
if (!captchaEnabled) {
return Result.success(respBuilder);
}
// 保存验证码信息
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.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", os);
} catch (IOException e) {
return Result.error(e.getMessage());
}
CaptchaCodeResp captchaCodeResp = respBuilder.uuid(uuid)
.img(Base64.encode(os.toByteArray()))
.build();
return Result.success(captchaCodeResp);
}
/**
*
*/
@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("验证码错误");
}
}
}

View File

@ -0,0 +1,113 @@
package com.muyu.gateway.utils;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Web
*
*
*/
@Log4j2
public class WebFrameworkUtils {
private static final String HEADER_TENANT_ID = "tenant-id";
private WebFrameworkUtils() {}
/**
* Gateway header HttpHeaders
*
* @param tenantId
* @param httpHeaders WebClient
*/
public static void setTenantIdHeader(Long tenantId, HttpHeaders httpHeaders) {
if (tenantId == null) {
return;
}
httpHeaders.set(HEADER_TENANT_ID, String.valueOf(tenantId));
}
public static Long getTenantId(ServerWebExchange exchange) {
String tenantId = exchange.getRequest().getHeaders().getFirst(HEADER_TENANT_ID);
return tenantId != null ? Long.parseLong(tenantId) : null;
}
/**
* JSON
*
* @param exchange
* @param object JSON
*/
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE否则会乱码
public static Mono<Void> writeJSON(ServerWebExchange exchange, Object object) {
// 设置 header
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
// 设置 body
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
return bufferFactory.wrap(JSONObject.toJSONString(object).getBytes());
} catch (Exception ex) {
ServerHttpRequest request = exchange.getRequest();
log.error("[writeJSON][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
/**
* IP
*
*
* @param exchange
* @param otherHeaderNames header
* @return IP
*/
public static String getClientIP(ServerWebExchange exchange, String... otherHeaderNames) {
String[] headers = { "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" };
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(headers, otherHeaderNames);
}
// 方式一,通过 header 获取
String ip;
for (String header : headers) {
ip = exchange.getRequest().getHeaders().getFirst(header);
if (!NetUtil.isUnknown(ip)) {
return NetUtil.getMultistageReverseProxyIp(ip);
}
}
// 方式二,通过 remoteAddress 获取
if (exchange.getRequest().getRemoteAddress() == null) {
return null;
}
ip = exchange.getRequest().getRemoteAddress().getHostString();
return NetUtil.getMultistageReverseProxyIp(ip);
}
/**
* Route
*
* @param exchange
* @return
*/
public static Route getGatewayRoute(ServerWebExchange exchange) {
return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
}
}

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -0,0 +1,75 @@
# Tomcat
server:
port: 8111
# nacos线上地址
nacos:
addr: 47.102.201.146:8848
user-name: nacos
password: nacos
namespace: cloud-2112
# Spring
spring:
application:
# 应用名称
name: cloud-gateway
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: ${nacos.addr}
# nacos用户名
username: ${nacos.user-name}
# nacos密码
password: ${nacos.password}
# 命名空间
namespace: ${nacos.namespace}
config:
# 服务注册地址
server-addr: ${nacos.addr}
# nacos用户名
username: ${nacos.user-name}
# nacos密码
password: ${nacos.password}
# 命名空间
namespace: ${nacos.namespace}
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
# 系统共享配置
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
# 系统环境Config共享配置
- application-config-${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: ${nacos.addr}
dataId: sentinel-cloud-gateway
groupId: DEFAULT_GROUP
namespace: ${nacos.namespace}
data-type: json
rule-type: gw-flow
knife4j:
gateway:
enabled: true
# 指定服务发现的模式聚合微服务文档,并且是默认`default`分组
strategy: discover
discover:
enabled: true
# 指定版本号(Swagger2|OpenAPI3)
version : openapi3
# 需要排除的微服务(eg:网关服务)
excluded-services:
- cloud-monitor

View File

@ -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/cloud-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.muyu" 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>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/cloud-gateway"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<property name="log.sky.pattern" value="%d{HH:mm:ss.SSS} %yellow([%tid]) [%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>
<!-- 使用gRpc将日志发送到skywalking服务端 -->
<appender name="GRPC_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log.sky.pattern}</Pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="GRPC_LOG"/>
</root>
<!-- 系统模块日志级别控制 -->
<logger name="com.muyu" 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>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/cloud-gateway"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<property name="log.sky.pattern" value="%d{HH:mm:ss.SSS} %yellow([%tid]) [%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>
<!-- 使用gRpc将日志发送到skywalking服务端 -->
<appender name="GRPC_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log.sky.pattern}</Pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="GRPC_LOG"/>
</root>
<!-- 系统模块日志级别控制 -->
<logger name="com.muyu" 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>

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -0,0 +1,75 @@
# Tomcat
server:
port: 8111
# nacos线上地址
nacos:
addr: 47.102.201.146:8848
user-name: nacos
password: nacos
namespace: cloud-2112
# Spring
spring:
application:
# 应用名称
name: cloud-gateway
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: ${nacos.addr}
# nacos用户名
username: ${nacos.user-name}
# nacos密码
password: ${nacos.password}
# 命名空间
namespace: ${nacos.namespace}
config:
# 服务注册地址
server-addr: ${nacos.addr}
# nacos用户名
username: ${nacos.user-name}
# nacos密码
password: ${nacos.password}
# 命名空间
namespace: ${nacos.namespace}
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
# 系统共享配置
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
# 系统环境Config共享配置
- application-config-${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: ${nacos.addr}
dataId: sentinel-cloud-gateway
groupId: DEFAULT_GROUP
namespace: ${nacos.namespace}
data-type: json
rule-type: gw-flow
knife4j:
gateway:
enabled: true
# 指定服务发现的模式聚合微服务文档,并且是默认`default`分组
strategy: discover
discover:
enabled: true
# 指定版本号(Swagger2|OpenAPI3)
version : openapi3
# 需要排除的微服务(eg:网关服务)
excluded-services:
- cloud-monitor

View File

@ -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/cloud-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.muyu" 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>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/cloud-gateway"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<property name="log.sky.pattern" value="%d{HH:mm:ss.SSS} %yellow([%tid]) [%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>
<!-- 使用gRpc将日志发送到skywalking服务端 -->
<appender name="GRPC_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log.sky.pattern}</Pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="GRPC_LOG"/>
</root>
<!-- 系统模块日志级别控制 -->
<logger name="com.muyu" 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>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/cloud-gateway"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
<property name="log.sky.pattern" value="%d{HH:mm:ss.SSS} %yellow([%tid]) [%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>
<!-- 使用gRpc将日志发送到skywalking服务端 -->
<appender name="GRPC_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${log.sky.pattern}</Pattern>
</layout>
</encoder>
</appender>
<root level="info">
<appender-ref ref="GRPC_LOG"/>
</root>
<!-- 系统模块日志级别控制 -->
<logger name="com.muyu" 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>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
artifactId=cloud-gateway
groupId=com.muyu
version=1.0.0

View File

@ -0,0 +1,30 @@
com\muyu\gateway\model\AccessLog$AccessLogBuilder.class
com\muyu\gateway\filter\BlackListUrlFilter.class
com\muyu\gateway\filter\CacheRequestFilter$Config.class
com\muyu\gateway\model\resp\CaptchaCodeResp$CaptchaCodeRespBuilder.class
com\muyu\gateway\config\properties\CaptchaProperties.class
com\muyu\gateway\filter\XssFilter$1.class
com\muyu\gateway\filter\CacheRequestFilter$CacheRequestGatewayFilter.class
com\muyu\gateway\service\impl\ValidateCodeServiceImpl.class
com\muyu\gateway\config\KaptchaTextCreator.class
com\muyu\gateway\config\CaptchaConfig.class
com\muyu\gateway\utils\WebFrameworkUtils.class
com\muyu\gateway\filter\AccessLogFilter$2.class
com\muyu\gateway\filter\XssFilter.class
com\muyu\gateway\handler\ValidateCodeHandler.class
com\muyu\gateway\handler\GatewayExceptionHandler.class
com\muyu\gateway\config\properties\IgnoreWhiteProperties.class
com\muyu\gateway\handler\SentinelFallbackHandler.class
com\muyu\gateway\config\properties\XssProperties.class
com\muyu\gateway\filter\AuthFilter.class
com\muyu\gateway\model\resp\CaptchaCodeResp.class
com\muyu\gateway\model\AccessLog.class
com\muyu\gateway\filter\BlackListUrlFilter$Config.class
com\muyu\gateway\filter\AccessLogFilter$1.class
com\muyu\gateway\filter\ValidateCodeFilter.class
com\muyu\gateway\CloudGatewayApplication.class
com\muyu\gateway\service\ValidateCodeService.class
com\muyu\gateway\filter\AccessLogFilter.class
com\muyu\gateway\config\GatewayConfig.class
com\muyu\gateway\config\RouterFunctionConfiguration.class
com\muyu\gateway\filter\CacheRequestFilter.class

View File

@ -0,0 +1,22 @@
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\utils\WebFrameworkUtils.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\CaptchaConfig.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\KaptchaTextCreator.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\model\resp\CaptchaCodeResp.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\ValidateCodeFilter.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\service\impl\ValidateCodeServiceImpl.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\GatewayConfig.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\handler\GatewayExceptionHandler.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\handler\ValidateCodeHandler.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\service\ValidateCodeService.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\AuthFilter.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\CloudGatewayApplication.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\CacheRequestFilter.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\handler\SentinelFallbackHandler.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\properties\IgnoreWhiteProperties.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\properties\XssProperties.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\XssFilter.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\BlackListUrlFilter.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\model\AccessLog.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\properties\CaptchaProperties.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\config\RouterFunctionConfiguration.java
D:\master\yjs-cloud-2112\cloud-gateway\src\main\java\com\muyu\gateway\filter\AccessLogFilter.java