commit 9c4d30cdd0ae76f9e12c238ee66fe4c972593bad Author: bai <173792339@qq.com> Date: Wed Aug 7 23:50:32 2024 +0800 初始化 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..2e89e50 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ee2c34b --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..872d41f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..78f492e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ca91814 --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/logs/cloud-gateway/error.log b/logs/cloud-gateway/error.log new file mode 100644 index 0000000..61ca459 --- /dev/null +++ b/logs/cloud-gateway/error.log @@ -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 diff --git a/logs/cloud-gateway/info.log b/logs/cloud-gateway/info.log new file mode 100644 index 0000000..d611285 --- /dev/null +++ b/logs/cloud-gateway/info.log @@ -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 - [,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 - [,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 - [,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 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bcb2c2d --- /dev/null +++ b/pom.xml @@ -0,0 +1,114 @@ + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + cloud-gateway + 1.0.0 + + + cloud-gateway网关模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + + com.alibaba.csp + sentinel-datasource-nacos + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springframework.cloud + spring-cloud-loadbalancer + + + + + pro.fessional + kaptcha + + + + + com.muyu + cloud-common-redis + + + + com.github.xiaoymin + knife4j-gateway-spring-boot-starter + 4.5.0 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + diff --git a/src/main/java/com/muyu/gateway/CloudGatewayApplication.java b/src/main/java/com/muyu/gateway/CloudGatewayApplication.java new file mode 100644 index 0000000..43c7ed9 --- /dev/null +++ b/src/main/java/com/muyu/gateway/CloudGatewayApplication.java @@ -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); + } +} diff --git a/src/main/java/com/muyu/gateway/config/CaptchaConfig.java b/src/main/java/com/muyu/gateway/config/CaptchaConfig.java new file mode 100644 index 0000000..557af8a --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/CaptchaConfig.java @@ -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 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath () { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.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; + } +} diff --git a/src/main/java/com/muyu/gateway/config/GatewayConfig.java b/src/main/java/com/muyu/gateway/config/GatewayConfig.java new file mode 100644 index 0000000..bf004bf --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/GatewayConfig.java @@ -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(); + } +} diff --git a/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java b/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7b0636d --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java @@ -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(); + } +} diff --git a/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java b/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java new file mode 100644 index 0000000..41b8e47 --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java @@ -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); + } +} diff --git a/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java b/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..60b1814 --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java @@ -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; + } +} diff --git a/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java b/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 0000000..d5ea46c --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java @@ -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 whites = new ArrayList<>(); + + public List getWhites () { + return whites; + } + + public void setWhites (List whites) { + this.whites = whites; + } +} diff --git a/src/main/java/com/muyu/gateway/config/properties/XssProperties.java b/src/main/java/com/muyu/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..31dcc6a --- /dev/null +++ b/src/main/java/com/muyu/gateway/config/properties/XssProperties.java @@ -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 excludeUrls = new ArrayList<>(); + + public Boolean getEnabled () { + return enabled; + } + + public void setEnabled (Boolean enabled) { + this.enabled = enabled; + } + + public List getExcludeUrls () { + return excludeUrls; + } + + public void setExcludeUrls (List excludeUrls) { + this.excludeUrls = excludeUrls; + } +} diff --git a/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java b/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java new file mode 100644 index 0000000..9ceec12 --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/AccessLogFilter.java @@ -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; + + +/** + * 网关的访问日志过滤器 + *

+ *

+ * 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> messageReaders = HandlerStrategies.withDefaults().messageReaders(); + + /** + * 打印日志 + * + * @param gatewayLog 网关日志 + */ + private void writeAccessLog(AccessLog gatewayLog) { + log.info("[网关日志:{}]", gatewayLog.toString()); + } + + @Override + public int getOrder() { + return -99; + } + + @Override + public Mono 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 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} 实现 + *

+ * 差别主要在于使用 modifiedBody 来读取 Request Body 数据 + */ + private Mono filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog gatewayLog) { + // 设置 Request Body 读取时,设置到网关日志 + ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders); + Mono modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> { + gatewayLog.setRequestBody(body); + return Mono.just(body); + }); + + // 创建 BodyInserter 对象 + BodyInserter, 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 writeWith(Publisher 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 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 中的方法 ========== + + /** + * 请求装饰器,支持重新计算 headers、body 缓存 + * + * @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 getBody() { + return outputMessage.getBody(); + } + }; + } + + // ========== 参考 ModifyResponseBodyGatewayFilterFactory 中的方法 ========== + + private byte[] readContent(List dataBuffers) { + // 合并多个流集合,解决返回体分段传输 + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + // 释放掉内存 + DataBufferUtils.release(join); + return content; + } + +} diff --git a/src/main/java/com/muyu/gateway/filter/AuthFilter.java b/src/main/java/com/muyu/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..47e073f --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/AuthFilter.java @@ -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 filter (ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpRequest.Builder mutate = request.mutate(); + + String url = request.getURI().getPath(); + // 跳过不需要验证的路径 + if (StringUtils.matches(url, ignoreWhite.getWhites())) { + return chain.filter(exchange); + } + String token = getToken(request); + if (StringUtils.isEmpty(token)) { + return unauthorizedResponse(exchange, "令牌不能为空"); + } + Claims claims = JwtUtils.parseToken(token); + if (claims == null) { + return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); + } + String userkey = JwtUtils.getUserKey(claims); + boolean islogin = redisService.hasKey(getTokenKey(userkey)); + if (!islogin) { + return unauthorizedResponse(exchange, "登录状态已过期"); + } + String userid = JwtUtils.getUserId(claims); + String username = JwtUtils.getUserName(claims); + if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) { + return unauthorizedResponse(exchange, "令牌验证失败"); + } + + // 设置用户信息到请求 + addHeader(mutate, SecurityConstants.USER_KEY, userkey); + addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); + addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); + // 内部请求来源参数清除 + removeHeader(mutate, SecurityConstants.FROM_SOURCE); + return chain.filter(exchange.mutate().request(mutate.build()).build()); + } + + private void addHeader (ServerHttpRequest.Builder mutate, String name, Object value) { + if (value == null) { + return; + } + String valueStr = value.toString(); + String valueEncode = ServletUtils.urlEncode(valueStr); + mutate.header(name, valueEncode); + } + + private void removeHeader (ServerHttpRequest.Builder mutate, String name) { + mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); + } + + private Mono unauthorizedResponse (ServerWebExchange exchange, String msg) { + log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); + } + + /** + * 获取缓存key + */ + private String getTokenKey (String token) { + return CacheConstants.LOGIN_TOKEN_KEY + token; + } + + /** + * 获取请求token + */ + private String getToken (ServerHttpRequest request) { + String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { + token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); + } + return token; + } + + @Override + public int getOrder () { + return -200; + } +} diff --git a/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java b/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java new file mode 100644 index 0000000..0096d4c --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java @@ -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 { + 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 blacklistUrl; + + private List blacklistUrlPattern = new ArrayList<>(); + + public boolean matchBlacklist (String url) { + return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); + } + + public List getBlacklistUrl () { + return blacklistUrl; + } + + public void setBlacklistUrl (List blacklistUrl) { + this.blacklistUrl = blacklistUrl; + this.blacklistUrlPattern.clear(); + this.blacklistUrl.forEach(url -> { + this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); + }); + } + } + +} diff --git a/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java b/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java new file mode 100644 index 0000000..3a09564 --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java @@ -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 { + 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 shortcutFieldOrder () { + return Collections.singletonList("order"); + } + + public static class CacheRequestGatewayFilter implements GatewayFilter { + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + // GET DELETE 不过滤 + HttpMethod method = exchange.getRequest().getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { + if (serverHttpRequest == exchange.getRequest()) { + return chain.filter(exchange); + } + return chain.filter(exchange.mutate().request(serverHttpRequest).build()); + }); + } + } + + static class Config { + private Integer order; + + public Integer getOrder () { + return order; + } + + public void setOrder (Integer order) { + this.order = order; + } + } +} diff --git a/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java b/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java new file mode 100644 index 0000000..c19c944 --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java @@ -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 { + 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 body = serverHttpRequest.getBody(); + AtomicReference bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + }); + return bodyRef.get(); + } +} diff --git a/src/main/java/com/muyu/gateway/filter/XssFilter.java b/src/main/java/com/muyu/gateway/filter/XssFilter.java new file mode 100644 index 0000000..4316d84 --- /dev/null +++ b/src/main/java/com/muyu/gateway/filter/XssFilter.java @@ -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 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 getBody () { + Flux body = super.getBody(); + return body.buffer().map(dataBuffers -> { + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + DataBufferUtils.release(join); + String bodyStr = new String(content, StandardCharsets.UTF_8); + // 防xss攻击过滤 + bodyStr = EscapeUtil.clean(bodyStr); + // 转成字节 + byte[] bytes = bodyStr.getBytes(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; + } +} diff --git a/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java b/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..ed5198e --- /dev/null +++ b/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java @@ -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 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); + } +} diff --git a/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java b/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java new file mode 100644 index 0000000..d93866f --- /dev/null +++ b/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java @@ -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 writeResponse (ServerResponse response, ServerWebExchange exchange) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); + } + + @Override + public Mono handle (ServerWebExchange exchange, Throwable ex) { + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + if (!BlockException.isBlockException(ex)) { + return Mono.error(ex); + } + return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); + } + + private Mono handleBlockedRequest (ServerWebExchange exchange, Throwable throwable) { + return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); + } +} diff --git a/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java b/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java new file mode 100644 index 0000000..f3b3206 --- /dev/null +++ b/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java @@ -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 { + @Autowired + private ValidateCodeService validateCodeService; + + @Override + public Mono 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)); + } +} diff --git a/src/main/java/com/muyu/gateway/model/AccessLog.java b/src/main/java/com/muyu/gateway/model/AccessLog.java new file mode 100644 index 0000000..b52eb3e --- /dev/null +++ b/src/main/java/com/muyu/gateway/model/AccessLog.java @@ -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 queryParams; + /** + * 请求体 + */ + private String requestBody; + /** + * 请求头 + */ + private MultiValueMap requestHeaders; + /** + * 用户 IP + */ + private String userIp; + + /** + * 响应体 + * + * 类似 ApiAccessLogCreateReqDTO 的 resultCode + resultMsg + */ + private String responseBody; + /** + * 响应头 + */ + private MultiValueMap 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(); + } +} diff --git a/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java b/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java new file mode 100644 index 0000000..bd12aad --- /dev/null +++ b/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java @@ -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; + +} diff --git a/src/main/java/com/muyu/gateway/service/ValidateCodeService.java b/src/main/java/com/muyu/gateway/service/ValidateCodeService.java new file mode 100644 index 0000000..25ed94e --- /dev/null +++ b/src/main/java/com/muyu/gateway/service/ValidateCodeService.java @@ -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; +} diff --git a/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java b/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java new file mode 100644 index 0000000..879d887 --- /dev/null +++ b/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java @@ -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("验证码错误"); + } + } +} diff --git a/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java b/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java new file mode 100644 index 0000000..2443e13 --- /dev/null +++ b/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java @@ -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 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); + } + +} diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..414c039 --- /dev/null +++ b/src/main/resources/bootstrap.yml @@ -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 diff --git a/src/main/resources/logback/dev.xml b/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..5ac21d0 --- /dev/null +++ b/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/logback/prod.xml b/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/logback/test.xml b/src/main/resources/logback/test.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/classes/banner.txt b/target/classes/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/target/classes/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/target/classes/bootstrap.yml b/target/classes/bootstrap.yml new file mode 100644 index 0000000..414c039 --- /dev/null +++ b/target/classes/bootstrap.yml @@ -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 diff --git a/target/classes/com/muyu/gateway/CloudGatewayApplication.class b/target/classes/com/muyu/gateway/CloudGatewayApplication.class new file mode 100644 index 0000000..0c12f3f Binary files /dev/null and b/target/classes/com/muyu/gateway/CloudGatewayApplication.class differ diff --git a/target/classes/com/muyu/gateway/config/CaptchaConfig.class b/target/classes/com/muyu/gateway/config/CaptchaConfig.class new file mode 100644 index 0000000..7a47f1d Binary files /dev/null and b/target/classes/com/muyu/gateway/config/CaptchaConfig.class differ diff --git a/target/classes/com/muyu/gateway/config/GatewayConfig.class b/target/classes/com/muyu/gateway/config/GatewayConfig.class new file mode 100644 index 0000000..5d6e39b Binary files /dev/null and b/target/classes/com/muyu/gateway/config/GatewayConfig.class differ diff --git a/target/classes/com/muyu/gateway/config/KaptchaTextCreator.class b/target/classes/com/muyu/gateway/config/KaptchaTextCreator.class new file mode 100644 index 0000000..61b54f4 Binary files /dev/null and b/target/classes/com/muyu/gateway/config/KaptchaTextCreator.class differ diff --git a/target/classes/com/muyu/gateway/config/RouterFunctionConfiguration.class b/target/classes/com/muyu/gateway/config/RouterFunctionConfiguration.class new file mode 100644 index 0000000..b41b43f Binary files /dev/null and b/target/classes/com/muyu/gateway/config/RouterFunctionConfiguration.class differ diff --git a/target/classes/com/muyu/gateway/config/properties/CaptchaProperties.class b/target/classes/com/muyu/gateway/config/properties/CaptchaProperties.class new file mode 100644 index 0000000..584c168 Binary files /dev/null and b/target/classes/com/muyu/gateway/config/properties/CaptchaProperties.class differ diff --git a/target/classes/com/muyu/gateway/config/properties/IgnoreWhiteProperties.class b/target/classes/com/muyu/gateway/config/properties/IgnoreWhiteProperties.class new file mode 100644 index 0000000..2259739 Binary files /dev/null and b/target/classes/com/muyu/gateway/config/properties/IgnoreWhiteProperties.class differ diff --git a/target/classes/com/muyu/gateway/config/properties/XssProperties.class b/target/classes/com/muyu/gateway/config/properties/XssProperties.class new file mode 100644 index 0000000..a942858 Binary files /dev/null and b/target/classes/com/muyu/gateway/config/properties/XssProperties.class differ diff --git a/target/classes/com/muyu/gateway/filter/AccessLogFilter$1.class b/target/classes/com/muyu/gateway/filter/AccessLogFilter$1.class new file mode 100644 index 0000000..17de4b9 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/AccessLogFilter$1.class differ diff --git a/target/classes/com/muyu/gateway/filter/AccessLogFilter$2.class b/target/classes/com/muyu/gateway/filter/AccessLogFilter$2.class new file mode 100644 index 0000000..e328071 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/AccessLogFilter$2.class differ diff --git a/target/classes/com/muyu/gateway/filter/AccessLogFilter.class b/target/classes/com/muyu/gateway/filter/AccessLogFilter.class new file mode 100644 index 0000000..1a170ed Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/AccessLogFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/AuthFilter.class b/target/classes/com/muyu/gateway/filter/AuthFilter.class new file mode 100644 index 0000000..bf18990 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/AuthFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/BlackListUrlFilter$Config.class b/target/classes/com/muyu/gateway/filter/BlackListUrlFilter$Config.class new file mode 100644 index 0000000..341018c Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/BlackListUrlFilter$Config.class differ diff --git a/target/classes/com/muyu/gateway/filter/BlackListUrlFilter.class b/target/classes/com/muyu/gateway/filter/BlackListUrlFilter.class new file mode 100644 index 0000000..b56cdbe Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/BlackListUrlFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/CacheRequestFilter$CacheRequestGatewayFilter.class b/target/classes/com/muyu/gateway/filter/CacheRequestFilter$CacheRequestGatewayFilter.class new file mode 100644 index 0000000..a81f0c6 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/CacheRequestFilter$CacheRequestGatewayFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/CacheRequestFilter$Config.class b/target/classes/com/muyu/gateway/filter/CacheRequestFilter$Config.class new file mode 100644 index 0000000..0bf7e1a Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/CacheRequestFilter$Config.class differ diff --git a/target/classes/com/muyu/gateway/filter/CacheRequestFilter.class b/target/classes/com/muyu/gateway/filter/CacheRequestFilter.class new file mode 100644 index 0000000..881d167 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/CacheRequestFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/ValidateCodeFilter.class b/target/classes/com/muyu/gateway/filter/ValidateCodeFilter.class new file mode 100644 index 0000000..5d3c421 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/ValidateCodeFilter.class differ diff --git a/target/classes/com/muyu/gateway/filter/XssFilter$1.class b/target/classes/com/muyu/gateway/filter/XssFilter$1.class new file mode 100644 index 0000000..b20fd64 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/XssFilter$1.class differ diff --git a/target/classes/com/muyu/gateway/filter/XssFilter.class b/target/classes/com/muyu/gateway/filter/XssFilter.class new file mode 100644 index 0000000..98bf9e9 Binary files /dev/null and b/target/classes/com/muyu/gateway/filter/XssFilter.class differ diff --git a/target/classes/com/muyu/gateway/handler/GatewayExceptionHandler.class b/target/classes/com/muyu/gateway/handler/GatewayExceptionHandler.class new file mode 100644 index 0000000..14a4853 Binary files /dev/null and b/target/classes/com/muyu/gateway/handler/GatewayExceptionHandler.class differ diff --git a/target/classes/com/muyu/gateway/handler/SentinelFallbackHandler.class b/target/classes/com/muyu/gateway/handler/SentinelFallbackHandler.class new file mode 100644 index 0000000..55ece32 Binary files /dev/null and b/target/classes/com/muyu/gateway/handler/SentinelFallbackHandler.class differ diff --git a/target/classes/com/muyu/gateway/handler/ValidateCodeHandler.class b/target/classes/com/muyu/gateway/handler/ValidateCodeHandler.class new file mode 100644 index 0000000..b9f5ac3 Binary files /dev/null and b/target/classes/com/muyu/gateway/handler/ValidateCodeHandler.class differ diff --git a/target/classes/com/muyu/gateway/model/AccessLog$AccessLogBuilder.class b/target/classes/com/muyu/gateway/model/AccessLog$AccessLogBuilder.class new file mode 100644 index 0000000..f49515b Binary files /dev/null and b/target/classes/com/muyu/gateway/model/AccessLog$AccessLogBuilder.class differ diff --git a/target/classes/com/muyu/gateway/model/AccessLog.class b/target/classes/com/muyu/gateway/model/AccessLog.class new file mode 100644 index 0000000..11ee670 Binary files /dev/null and b/target/classes/com/muyu/gateway/model/AccessLog.class differ diff --git a/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp$CaptchaCodeRespBuilder.class b/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp$CaptchaCodeRespBuilder.class new file mode 100644 index 0000000..88a7b05 Binary files /dev/null and b/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp$CaptchaCodeRespBuilder.class differ diff --git a/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp.class b/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp.class new file mode 100644 index 0000000..f896919 Binary files /dev/null and b/target/classes/com/muyu/gateway/model/resp/CaptchaCodeResp.class differ diff --git a/target/classes/com/muyu/gateway/service/ValidateCodeService.class b/target/classes/com/muyu/gateway/service/ValidateCodeService.class new file mode 100644 index 0000000..49f5665 Binary files /dev/null and b/target/classes/com/muyu/gateway/service/ValidateCodeService.class differ diff --git a/target/classes/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.class b/target/classes/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.class new file mode 100644 index 0000000..3ef4a18 Binary files /dev/null and b/target/classes/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.class differ diff --git a/target/classes/com/muyu/gateway/utils/WebFrameworkUtils.class b/target/classes/com/muyu/gateway/utils/WebFrameworkUtils.class new file mode 100644 index 0000000..64e7bfc Binary files /dev/null and b/target/classes/com/muyu/gateway/utils/WebFrameworkUtils.class differ diff --git a/target/classes/logback/dev.xml b/target/classes/logback/dev.xml new file mode 100644 index 0000000..5ac21d0 --- /dev/null +++ b/target/classes/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/target/classes/logback/prod.xml b/target/classes/logback/prod.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/target/classes/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/classes/logback/test.xml b/target/classes/logback/test.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/target/classes/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/cloud-gateway.jar b/target/cloud-gateway.jar new file mode 100644 index 0000000..b69e4d4 Binary files /dev/null and b/target/cloud-gateway.jar differ diff --git a/target/cloud-gateway.jar.original b/target/cloud-gateway.jar.original new file mode 100644 index 0000000..b87d649 Binary files /dev/null and b/target/cloud-gateway.jar.original differ diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..2334697 --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=cloud-gateway +groupId=com.muyu +version=1.0.0 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..fd56000 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -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 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..890cfe7 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -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