当前位置: 首页 > news >正文

【动态路由】系统web url整合系列【springcloud-gateway实现】【不改hosts文件版】组件一:多个Eureka路由过滤器

需求

      实现URL web资源整合,实现使用一个web地址访问多个web资源

方案

      本方案使用SpringCloud Gateway实现,不需要在hosts文件加添加域名映射(也不需要定义一系列域名),通过url路径来将请求转发到不同的Web资源

      如:http://${proxyIP}:${proxyPORT}/nacos1 

             转发到

             http://${nacos1IP}:${nacos1PORT}     

 

1、优点

  • 不需要用户端修改hosts文件(或是域名服务支持)

2、缺点

  • session信息可能会生产覆盖,存在session失效和横向越权风险

小知识:

        浏览器的session是按ip+端口(或域名+端口)作为唯一key存的

环境

spring-cloud 2024.0.0

spring-cloud gateway 4.2.0

实现代码

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><!-- 3.4.0  3.3.6  3.1.5  3.0.12  --><version>3.4.0</version><!--  lookup parent from repository  --><relativePath/></parent><groupId>person.daizd</groupId><artifactId>gwproxy</artifactId><version>1.0-SNAPSHOT</version><!-- 统一管理jar包版本 --><properties><java.version>17</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><!--   3.4.0   3.3.6  --><spring-boot.version>3.4.0</spring-boot.version><!-- 2024.0.0   2023.0.4    --><spring-cloud.version>2024.0.0</spring-cloud.version><!--  2023.0.3.2   2023.0.1.3    2022.0.0.0    2021.1   2021.0.6.1   2021.0.5.0   --><spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version><!--  springcloud 2020.02 以后需要单独引入     4.1.4   4.0.5  3.1.9  3.0.6  --><spring-cloud-starter-bootstrap.version>4.1.4</spring-cloud-starter-bootstrap.version><!-- 0.3.0-RC 适配springboot3    0.2.12 适配springboot2    0.1.10 适配springboot1   --><nacos-config-spring-boot-starter.version>0.3.0-RC</nacos-config-spring-boot-starter.version><!--        <junit.version>5.8.2</junit.version>--><log4j.version>1.2.17</log4j.version><lombok.version>1.18.28</lombok.version><commons-lang3.version>3.14.0</commons-lang3.version><io.micrometer.version>1.9.17</io.micrometer.version><!--  pmd-core 7.0.0:  3.25.0  3.24.0  3.23.0  3.22.0pmd-core 6.55.0:  3.21.2    3.13.0  3.11.0 copote  --><maven-pmd-plugin.version>3.26.0</maven-pmd-plugin.version><maven-clean-plugin.version>3.26.0</maven-clean-plugin.version><fastjson2.version>2.0.53</fastjson2.version></properties><dependencies><!--gateway的依赖 springcloud开发--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>${spring-cloud-starter-bootstrap.version}</version></dependency><!--   之前工程即没有引入 SpringCloud,也没有引入 SpringCloudAlibaba  --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>${spring-cloud-alibaba.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>compile</scope></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>${fastjson2.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${commons-lang3.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-resolver-dns-native-macos</artifactId><version>4.1.115.Final</version><classifier>osx-aarch_64</classifier></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.22</version>
<!--            <scope>test</scope>--></dependency><!-- 测试相关 这个包含junit5  --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId><version>${io.micrometer.version}</version><scope>compile</scope></dependency></dependencies></dependencyManagement><build><plugins><!-- 用于支持热部署 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!-- 表示它将创建(fork)一个新的JVM来运行编译器。 spring boot plugin 3.0.0没有这个属性了<fork>true</fork> --><addResources>true</addResources></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.3.1</version><executions><execution><id>copy-resource</id><phase>compile</phase><goals><goal>copy-resources</goal></goals><configuration><outputDirectory>${basedir}/target</outputDirectory><resources><resource><!-- 文件地址 --><directory>${basedir}/src/main/resources</directory><includes><include>config.ini</include></includes></resource><resource><!-- 文件地址 --><directory>${basedir}/src/main/resources</directory><includes><include>cookie.png</include></includes></resource></resources></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><target>${maven.compiler.target}</target><source>${maven.compiler.source}</source><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>3.3.1</version><executions><execution><phase>verify</phase><goals><goal>jar-no-fork</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-clean-plugin</artifactId><version>3.3.2</version></plugin><!--  PMD toolkit  --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-pmd-plugin</artifactId><version>3.26.0</version></plugin><!--- 支持单元测试 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.2</version></plugin><!--- 代码覆盖率工具 --><plugin><groupId>org.jacoco</groupId><artifactId>jacoco-maven-plugin</artifactId><version>0.8.12</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><version>3.7.1</version><executions><execution><id>copy-dependencies</id><phase>package</phase><goals><goal>copy-dependencies</goal></goals><configuration><outputDirectory>${project.build.directory}/lib</outputDirectory></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.4.2</version><configuration><archive><manifest><addClasspath>true</addClasspath><classpathPrefix>lib/</classpathPrefix><mainClass>person.daizd.GatewayApplication</mainClass></manifest></archive></configuration></plugin></plugins></build></project>

配置

# 这里有很多组件:eureka、apollo、nacos、skywalking等,且每一类组件都有多套(如:有的各省一套)

# id与uri的映射
gwproxy.routes.cteureka_local=http://eureka:eureka@127.0.0.1:8200
gwproxy.routes.cmdb_local=http://127.0.0.1:8071# eureka
gwproxy.routes.cteureka_sh_test=http://eureka:eureka@10.130.***.157:8200
gwproxy.routes.sxeureka_sh_test=http://eureka:eureka@10.130.***.157:8206
gwproxy.routes.fjeureka_sh_test=http://eureka:eureka@10.130.***.157:8207gwproxy.routes.zjeureka_gcgz_nmb3_prod=http://10.141.***.240:20222# apollo
gwproxy.routes.apollo_sh_test=http://10.130.***.157:28003
gwproxy.routes.apollo_sh_sit=http://10.130.***.112:30001gwproxy.routes.ctapollo__nmb3_prod=http://10.141.***.240:20400
gwproxy.routes.lnapollo_idc01_nmb3_prod=http://10.141.***.240:20403# sentinel
gwproxy.routes.lnsentinel_idc01_nmb3_prod=http://10.141.***.240:23903
gwproxy.routes.jssentinel_idc01_nmb3_prod=http://10.141.***.240:23915
gwproxy.routes.ahsentinel_idc01_nmb3_prod=http://10.141.***.240:23923# apisix
# http://${gwproxy.root_uri}/apisix/index.html?app=apisix_sh_test
gwproxy.routes.apisix_sh_test=http://10.130.***.157:9000gwproxy.routes.ctapisix_gz_gzb1_prod=http://10.***.5.131:32065gwproxy.routes.ctapisix_idc01_nmb3_prod=http://10.141.***.240:31600
gwproxy.routes.lnapisix_idc01_nmb3_prod=http://10.141.***.240:31603# prometheus 
# http://${gwproxy.root_uri}/prometheus_sh_test/graph
gwproxy.routes.prometheus_sh_test=http://10.130.***.136:9090
gwproxy.routes.prometheus_idc01_gzb1c1_prod=http://10.***.32.245:30006
gwproxy.routes.prometheus_idc01_gzb1c2_prod=http://10.***.32.246:30006
gwproxy.routes.prometheus_idc01_gzb1c3_prod=http://10.***.32.247:30006gwproxy.routes.prometheus_idc01_nmb3c1_prod=http://10.141.***.240:30006
gwproxy.routes.prometheus_idc01_nmb3c2_prod=http://10.141.***.241:30006
gwproxy.routes.prometheus_idc01_nmb3c3_prod=http://10.141.***.242:30006gwproxy.routes.prometheus_sh_crm120=http://10.130.***.120:9090
gwproxy.routes.prometheus_sh_crm121=http://10.130.***.121:9090
gwproxy.routes.prometheus_gz_saas190=http://10.***.31.190:30900# nacos
# http://${gwproxy.root_uri}/nacos_sh_test/nacos/#/login
gwproxy.routes.nacos_sh_test=http://10.130.***.92:18848# gateway
gwproxy.routes.ahgateway_idc01_nmb3_prod=http://10.141.***.242:23266
gwproxy.routes.jsgateway_idc01_nmb3_prod=http://10.141.***.216:32015
gwproxy.routes.bjgateway_idc01_nmb3_prod=http://10.141.***.209:32008
gwproxy.routes.lngateway_idc01_nmb3_prod=http://10.141.***.204:30003# elasticsearch
gwproxy.routes.elasticsearch_gz_247=http://10.***.6.247:9200# kibana     待优化KibanaRoute1 用于同时支持kibana 直接访问和代理访问,不过官方说只能2选一,优化难度大
# /kibana_local/login?next=/kibana_local/
gwproxy.routes.kibana_local=http://esserver:5601# /kibana_sh_test/login?next=/kibana_sh_test/
gwproxy.routes.kibana_sh_cs134_test=http://10.130.***.134:15601
gwproxy.routes.kibana_sh_jg92_test=http://10.130.***.92:5602# /gzkibana_5601/login?next=/gzkibana_5601/
# /gzkibana_5601/login?msg=LOGGED_OUT
gwproxy.routes.kibana_gz_5601_prod=http://10.***.6.247:5601
gwproxy.routes.kibana_gz_5602_prod=http://10.***.6.247:5602
gwproxy.routes.kibana_gz_5603_prod=http://10.***.6.247:5603# /nmkibana_5601/login?next=%2F
gwproxy.routes.kibana_nm_5601_prod=http://10.141.137.***:5601
gwproxy.routes.kibana_nm_5602_prod=http://10.141.137.***:5601
gwproxy.routes.kibana_nm_5603_prod=http://10.141.137.***:5601# timetask  当前可参考kibana 通过改根路径解决(这里直接改应用包名)
# http://${gwproxy.root_uri}/timetask/toLogin
# http://${gwproxy.root_uri}/cttimetask_idc01_nmb3_prod/timetask/toLogin
# 因静态资源命名冲突,所以两种(改工程名与直接支持)配置不能同时存在
#
gwproxy.routes.timetask=http://10.141.***.240:22400
#gwproxy.routes.timetask-xxl-job=http://10.141.***.240:22400
gwproxy.routes.cttimetask_idc01_nmb3_prod=http://10.141.***.240:22400
gwproxy.routes.cttimetask_idc01_gzb1_prod=http://10.***.5.131:22400# Grafana
# http://${gwproxy.root_uri}/grafana_sh_prod/login
gwproxy.routes.grafana_sh_prod=http://10.130.***.121:50001# Ambari (Hadoop - Hbase )   
# http://${gwproxy.root_uri}/ambari_sh_prod/#/login
gwproxy.routes.ambari_sh_prod=http://10.***.6.231:8080# kafka-map   
gwproxy.routes.kafkamap_sh_jg92_test=http://10.130.***.92:8083
gwproxy.routes.kafkamap_sh_prod=http://10.130.***.121:8093# cmak
# /cmak_sh_jg93_test
gwproxy.routes.cmak_sh_jg93_test=http://10.130.***.93:8094
# 没启或没安装
gwproxy.routes.cmak_sh_prod=http://10.130.***.121:8094# skywalking
gwproxy.routes.skywalking_sh_jg131_test=http://10.130.***.131:30198
# 
gwproxy.routes.skywalking_xw_nm_prod=http://10.141.***.41:31051
gwproxy.routes.skywalking_xw_gz_prod=hhttp://10.***.6.204:31051
gwproxy.routes.skywalking_idc01_nmb3_prod=hhttp://10.141.***.240:31051# pinpoint
gwproxy.routes.pinpoint_idc01_gzb1_prod=http://10.***.5.131:20900
gwproxy.routes.pinpoint_idc01_nmb3_prod=http://10.141.***.95:20900# redmine
gwproxy.routes.redmine_idc01_nmb3_prod=http://10.142.***.247:8080# harbor
gwproxy.routes.harbor_idc01_gzb1_prod=http://10.***.100.18:8021# jenkins
gwproxy.routes.jenkins_sh_cs124_prod=http://10.130.***.124:1906# nexus
gwproxy.routes.nexus_sh_jg122_prod=http://10.130.***.122:18081# gitlab
gwproxy.routes.gitlab_sh_jg122_prod=http://10.130.***.122:8082# kubesphere
gwproxy.routes.kubesphere_sh_cs131_prod=http://10.130.***.131:30880# nightingale 
# nightingale_sh_test/login   
# nightingale_sh_test/metric/explorer
# 10.130.***.120 nightingale.sh.test
# 10.130.***.120 nightingale.sh.prod
gwproxy.routes.nightingale_sh_test=http://10.130.***.136:17000
gwproxy.routes.nightingale_sh_prod=http://10.130.***.121:17000# ~~~~~~~~~~~~~~~~   业务应用 begin   ~~~~~~~~~~~~~~~~~~~
# http://${gwproxy.root_uri}/portal1_sh_prod/login
gwproxy.routes.portal1_sh_prod=http://10.130.***.121:8082gwproxy.routes.web2_sh_test=http://10.130.***.124:8201
# http://${gwproxy.root_uri}/web2_idc01_nmb3_prod/web2/
gwproxy.routes.web2_idc01_nmb3_prod=http://10.***.228.140:32100

配置类

/*** @Description: 从nacos获取配置* @Author: dand* @CreateDate: 2024/12/14 5:43 PM* @Version: 1.0*/import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@ConfigurationProperties(prefix = "gwproxy")
@Getter
@Setter
@RefreshScope
public class NacosMappingConfig {private Map<String, String> routes;}

动态路由实现【重点】

# 这里涉及很多组件,本文先上eureka的代码(其他组件支持代码读者可自行注释掉),后续更新文章其他组件相关动态路由实现代码会陆续上架

package person.daizd.config;import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import person.daizd.config.route.*;
import person.daizd.filters.factory.*;import java.util.Iterator;
import java.util.Map;
import java.util.Set;/**** @Author dand   QQ:413881461* @Slogan 致敬大师,致敬未来的你*/
@Slf4j
@Configuration
public class GatewayConfig {@Value("${gwproxy.root_uri}")private String rootUri;@Autowiredprivate NacosMappingConfig nacosMappingConfig;@Autowiredprivate CmdbFilterFactory1 cmdbFilterFactory1;@Autowiredprivate CmdbFilterFactory2 cmdbFilterFactory2;@Autowiredprivate ApisixFilterFactory1 apisixFilterFactory1;@Autowiredprivate ApisixFilterFactory2 apisixFilterFactory2;@Autowiredprivate EurekaFilterFactory1 eurekaFilterFactory1;@Autowiredprivate EurekaFilterFactory2 eurekaFilterFactory2;@Autowiredprivate N9eFilterFactory1 n9eFilterFactory1;@Autowiredprivate N9eFilterFactory2 n9eFilterFactory2;//    @Autowired
//    private KibanaFilterFactory1 kibanaFilterFactory1;
//    @Autowired
//    private KibanaFilterFactory2 kibanaFilterFactory2;@Autowiredprivate TimeTaskFilterFactory1 timeTaskFilterFactory1;@Autowiredprivate TimeTaskFilterFactory2 timeTaskFilterFactory2;@Bean
//    @RefreshScopepublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) throws NacosException {log.debug("nacosMappingConfig:{}",nacosMappingConfig.getRoutes());// 需要换成配置中取
//        Map<String,String> map = new HashMap<String,String>();Map<String,String> map = nacosMappingConfig.getRoutes();
//        map.put("cteureka_local","http://eureka:eureka@127.0.0.1:8200");
//        map.put("cmdb_local","http://127.0.0.1:8071");RouteLocatorBuilder.Builder innerBuilder = builder.routes();Set<Map.Entry<String, String>> set = map.entrySet();Iterator<Map.Entry<String, String>> it = set.iterator();/***  特别注意:*  1、路径名称不能与已有资源名重命*  2、相互之间不能是包含(子集)关系***/// 先统一处理所有eurekawhile (it.hasNext()) {Map.Entry<String, String> entry = it.next();if(entry.getKey().contains("eureka")){innerBuilder = EurekaRoute.route(eurekaFilterFactory1, eurekaFilterFactory2,innerBuilder,    entry,  rootUri);}// 防止不重复处理 用 else ifelse if(entry.getKey().contains("cmdb")){innerBuilder = CmdbRoute.route(cmdbFilterFactory1, cmdbFilterFactory2,innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("sentinel")){innerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("apisix")){innerBuilder = ApisixRoute2.route(apisixFilterFactory1,apisixFilterFactory2,innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("ddal")){innerBuilder = CmdbRoute.route(cmdbFilterFactory1, cmdbFilterFactory2,innerBuilder,    entry,  rootUri);}/* 待优化KibanaRoute1 用于同时支持kibana 直接访问和代理访问,不过官方说只能2选一,优化难度大另外也可开通SSO能力后研究其他方案前端使用了react,采用nodejs部署*/else if(entry.getKey().contains("kibana")){innerBuilder = KibanaRoute2.route(innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("elasticsearch")){innerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("nacos")){innerBuilder = CmdbRoute.route(cmdbFilterFactory1, cmdbFilterFactory2,innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("prometheus")){innerBuilder = CmdbRoute.route( cmdbFilterFactory1, cmdbFilterFactory2,innerBuilder,    entry,  rootUri);}// @todo  n9e 前端使用了React和D3.js, 等官方支持修改根路径(改为与配置名一致)或 二次n9e前端工程(https://github.com/n9e/fe)else if(entry.getKey().contains("nightingale")){innerBuilder = N9eRoute.route(    n9eFilterFactory1, n9eFilterFactory2,innerBuilder,    entry,  rootUri);}/** 方案一: 改timetask 上下文(应用名/根路径)-- 推荐(kibana 方案)* 方案二: 直接支持(不调整应用) -- 目前与n9e一样存在类似(菜单路径)问题*/else if(entry.getKey().contains("timetask")){innerBuilder = KibanaRoute2.route(innerBuilder,    entry,  rootUri);//                innerBuilder = TimeTaskRoute.route( timeTaskFilterFactory1, timeTaskFilterFactory2,
//                        innerBuilder,    entry,  rootUri);}// @todoelse if(entry.getKey().contains("grafana")){  // /grafana_sh_prod/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}else if(entry.getKey().contains("ambari")){  // /ambari_sh_prod/#/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// eureka √else if(entry.getKey().contains("kafkamap")){  // /kafkamap_sh_jg92_test/#/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// cmak 存在类似n9e的菜单路径问题else if(entry.getKey().contains("cmak")){  // /cmak_sh_jg93_testinnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 补充了一个js待验证else if(entry.getKey().contains("skywalking")){  // /skywalking_sh_jg131_test/General-Service/ServicesinnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 初步让为 √else if(entry.getKey().contains("pinpoint")){  // /pinpoint_gcgz_gzb1_prod/#/maininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 120网络不通else if(entry.getKey().contains("redmine")){  // /redmine_gcgz_nmb3_prod/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 初步让为harbor上下文路径可以修改else if(entry.getKey().contains("harbor")){  // /harbor_gcgz_gzb1_prod/harbor/sign-ininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 能登陆,但主页面显示不正常else if(entry.getKey().contains("jenkins")){  // /jenkins_sh_cs124_prod/jenkins/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 登陆有问题else if(entry.getKey().contains("nexus")){  // /nexus_sh_jg122_prod/nexus/#welcomeinnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 静态资源访问有问题else if(entry.getKey().contains("gitlab")){  // /gitlab_sh_jg122_prod/users/sign_ininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 未知错误else if(entry.getKey().contains("kubesphere")){  // /kubesphere_sh_cs131_prod/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}// 可以登陆,静态资源加载有问题else if(entry.getKey().contains("opsportal")){  // /opsportal_sh_prod/logininnerBuilder = CommonRoute.route(innerBuilder,    entry,  rootUri);}};return innerBuilder.build();}}

eureka动态路由实现类

package person.daizd.config.route;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import person.daizd.filters.factory.*;import java.util.Map;
import java.util.function.Function;/*** 路由** @Author: daizd* @CreateDate: 2024/12/13 9:28 PM* @Version: 1.0*/
@Slf4j
public class EurekaRoute {public static RouteLocatorBuilder.Builder route(EurekaFilterFactory1 filterFactory1,EurekaFilterFactory2 filterFactory2,RouteLocatorBuilder.Builder innerBuilder,Map.Entry<String, String> entry,String rootUri){// 每个eureka要配两个路由, 第一个innerBuilder = innerBuilder.route( entry.getKey()+"_1", // idr -> r.path("/"+entry.getKey()+"/**")       //   --   /cteureka/**.filters( f -> f.stripPrefix(1).preserveHostHeader().saveSession()  ).uri( entry.getValue() ));log.debug("Referer:"+rootUri+"/"+entry.getKey()+"[^\\s]*" );log.debug("uri:"+entry.getValue() );// 每个eureka要配两个路由,第二个  modifyRedirectFilterFactoryinnerBuilder.route( entry.getKey()+"_2", // idr -> r.header("Referer",rootUri+"/"+entry.getKey()+"[^\\s]*" )
//                        .filters( f -> f.stripPrefix(0).preserveHostHeader() ).filters(new Function<GatewayFilterSpec, UriSpec>() {@Overridepublic UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {return gatewayFilterSpec.filter(filterFactory2.apply(applyFilter("app",entry.getKey() )));}}).uri( entry.getValue() ));return innerBuilder;}/*** 构造config** @param headerName* @param headerValue* @return NameValueConfig*/private static AbstractNameValueGatewayFilterFactory.NameValueConfig applyFilter(String headerName, String headerValue) {AbstractNameValueGatewayFilterFactory.NameValueConfig config = new AbstractNameValueGatewayFilterFactory.NameValueConfig();config.setName(headerName);config.setValue(headerValue);return config;}
}

GatewayFilterFactory1【保留】

package person.daizd.filters.factory;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;/*** 用在路由1上** @Author: dand* @CreateDate: 2024/12/8 7:49 PM* @Version: 1.0**  没有用到*/
@Component
@Slf4j
public class EurekaFilterFactory1 extends AbstractNameValueGatewayFilterFactory  {@Value("${gwproxy.root_uri}")private String rootUri;/*** 1、如果是重定向则定向到配置参数(应用名)对应的路径* 2、如果不是重定向,且第一级路径与应用名不一致,则修改为一致** @param config* @return*/@Overridepublic GatewayFilter apply( NameValueConfig config) {return (exchange, chain) ->chain.filter(exchange).then(Mono.fromRunnable(() -> {ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();HttpStatusCode statusCode = response.getStatusCode();String name = config.getName();String value = config.getValue();String rootUrl = rootUri+"/"+config.getValue();if (statusCode == HttpStatus.SEE_OTHER || statusCode == HttpStatus.FOUND) {String location = response.getHeaders().getFirst(HttpHeaders.LOCATION);// 修改location的逻辑if(response.getHeaders().containsKey("Set-Cookie")
//                                || request.getHeaders().containsKey("Cookie")){// 有 Set-Cookie  和 cookie都认为已登陆response.getHeaders().set(HttpHeaders.LOCATION, rootUrl );log.info("response.getHeaders().containsKey(\"Set-Cookie\") , to rootUrl:{} ",rootUrl);}else{response.getHeaders().set(HttpHeaders.LOCATION, rootUrl+"/signin" );log.info(" else : {}/signin ",rootUrl);}}}));}@Overridepublic Class<NameValueConfig> getConfigClass() {// 如果你有配置属性,返回配置属性的类;如果没有,返回nullreturn NameValueConfig.class; // 或者 return YourConfigProperties.class;}
}

GatewayFilterFactory2

package person.daizd.filters.factory;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import java.net.URI;
import java.util.List;/*** 用在路由2上** @Author: dand* @CreateDate: 2024/12/8 7:49 PM* @Version: 1.0*/
@Component
@Slf4j
public class EurekaFilterFactory2 extends AbstractNameValueGatewayFilterFactory  {@Value("${gwproxy.root_uri}")private String rootUri;/*** 1、如果是重定向则定向到配置参数(应用名)对应的路径* 2、如果不是重定向,且第一级路径与应用名不一致,则修改为一致** @param config* @return*/@Overridepublic GatewayFilter apply( NameValueConfig config) {return (exchange, chain) ->chain.filter(exchange).then(Mono.fromRunnable(() -> {ServerHttpResponse response = exchange.getResponse();HttpStatusCode statusCode = response.getStatusCode();String remoteIp = exchange.getRequest().getRemoteAddress().getHostName();log.info("remoteIp:"+remoteIp);// "http://127.0.0.1:8089/oldcmdb";String rootUrl = rootUri+"/"+config.getValue();System.out.println("statusCode:"+statusCode);
//                    FluxOnAssembly a;if (statusCode != HttpStatus.SEE_OTHER && statusCode != HttpStatus.FOUND) {ServerHttpRequest request = exchange.getRequest();URI uri = request.getURI() ;String path = request.getPath().value();HttpHeaders headers = request.getHeaders();List<String> referer = headers.get("referer");log.info("path:{}", uri.getPath() );if( referer!=null&& referer.size()>0&& referer.get(0).indexOf( rootUrl ) != -1 // referer 包含:http://127.0.0.1:8089/oldcmdb&& uri.getPath().indexOf( "/"+config.getValue() ) == -1  // uri 不包含 /oldcmdb&& (uri.getPath().equalsIgnoreCase("/")|| uri.getPath().equalsIgnoreCase("/lastn")   )) //  是html页面{// 重定向到 RewritePath=/(?<segment>.*), /oldcmdb/$\{segment}exchange.getResponse().setStatusCode(HttpStatus.FOUND);exchange.getResponse().getHeaders().setLocation( URI.create( "/"+config.getValue()+ path) );log.info("uri.getPath().indexOf( \".html\") ");}}}));}@Overridepublic Class<NameValueConfig> getConfigClass() {// 如果你有配置属性,返回配置属性的类;如果没有,返回nullreturn NameValueConfig.class; // 或者 return YourConfigProperties.class;}
}

效果说明

为了让读者更好的读懂上面的代码,下面给出静态配置实现(上面代码动态实现了该效果且更灵活)

server:port: 8089gwproxy:root_uri: ${root_uri:http://127.0.0.1:8089}spring:application:name: api-gatewaycloud:gateway:httpclient:response-timeout: PT60Sconnect-timeout: 2000# 链接池配置pool:# 最大连接数max-connections: 10000# 仅对于FIXED类型,等待获取线程的最长时间(毫秒)acquire-timeout: 1000# 最大空闲时间max-idle-time: PT10S# 设置固定链接池type: fixeddiscovery:locator:enabled: truelower-case-service-id: true#路由规则routes:#  ~~~~~~~~  eureka  ~~~~~~~~~~~~~~~~- id: cteureka_route_1 #  8100000    #路由的ID,没有固定规则但要求唯一,建议配合服务名uri: http://eureka:eureka@127.0.0.1:8200          #匹配后提供服务的路由地址predicates:- Path=/cteureka/**         # 断言,路径相匹配的进行路由filters:- StripPrefix=1  # 转发之前去掉第一层路径- id: cteureka_route_2uri: http://eureka:eureka@127.0.0.1:8200          #匹配后提供服务的路由地址predicates:- Header=Referer,${gwproxy.root_uri}/cteurekamanagement:info: # 显示任意的应用信息,默认关闭  springBoot版本:2.7.15  GA如果是<2.7.5 的版本默认是开启的env:enabled: trueendpoints:web:exposure:include: "*"base-path: /whoami
info:app:name: gatewayencoding: "@project.build.sourceEncoding@"java:source: "@java.version@"target: "@java.version@"company:name: www.asiainfo.comdebug: true

附件一:整合界面地址效果截图

附件二:springcloud gateway官方文档

Spring Cloud Gateway 帮助文档

Spring Cloud Gateway 官方首页

相关文章:

【动态路由】系统web url整合系列【springcloud-gateway实现】【不改hosts文件版】组件一:多个Eureka路由过滤器

需求 实现URL web资源整合&#xff0c;实现使用一个web地址访问多个web资源 方案 本方案使用SpringCloud Gateway实现&#xff0c;不需要在hosts文件加添加域名映射&#xff08;也不需要定义一系列域名&#xff09;&#xff0c;通过url路径来将请求转发到不同的Web资源 如&…...

Mybatis-扩展功能

逻辑删除乐观锁 MyBatisPlus从入门到精通-3&#xff08;含mp代码生成器&#xff09; Db静态工具类 Spring依赖循环问题 代码生成器 MybatisPlus代码生成器 枚举处理器 我们这里用int来存储状态 需要注解&#xff0c;很不灵活 希望用枚举类来代替这个Integer 这样的话我…...

基于SpringBoot实现的大学社团平台系统实现功能六

一、前言介绍&#xff1a; 1.1 项目摘要 随着高校社团活动的日益丰富和多样化&#xff0c;学生对于社团管理和参与的需求也在不断增加。传统的社团管理方式往往存在效率低下、信息不透明等问题&#xff0c;无法满足现代学生对于便捷、高效社团管理的需求。因此&#xff0c;利…...

电子电气架构 --- 机器学习推动车载雷达的发展

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...

python从入门到进去

python从入门到进去 第一章、软件和工具的安装一、安装 python 解释器二、安装 pycharm 第二章、初识 python一、注释可分三种二、打印输入语句三、变量1、基本数据类型1.1、整数数据类型 int1.2、浮点数数据类型 float1.3、布尔数据类型 boolean1.4、字符串数据类型 string 2、…...

智能化客户画像构建管理:AI视频监控在大型商场的技术

前言&#xff1a;某商家为了优化卖场服务与营销策略&#xff0c;希望通过非侵入式手段获取客户画像&#xff0c;不仅可以帮助卖场提升服务质量、优化营销策略&#xff0c;还能通过数据驱动的方式提升销售业绩和顾客满意度&#xff0c;为卖场的长期发展奠定坚实的基础。 具体需求…...

php 拼接字符串

php 拼接字符串 .连字符"Hello, $name" 双引号内会解析变量"Hello, {$name}Doe" 使用花括号可以更明确标识变量名sprintf("Hello, %s", $name) 使用sprintfheredoc语法&#xff0c;同样支持变量的解析$html <<<EOT <p>Hello, $…...

Deepseek实用万能提问模板

一&#xff0c;背景需求约束条件 背景:提供与问题相关的时间、地点、人物、事件等信息&#xff0c;帮助 DeepSeek 更好地理解问题的情境。 需求:清晰明确地阐述你希望 DeepSeek完成的任务或提供的信息。 约束条件:可根据具体情况&#xff0c;对回答的范围、格式、字数等进行…...

MySQL、MariaDB 和 TDSQL 的区别

MySQL、MariaDB 和 TDSQL 是三种不同的数据库管理系统&#xff0c;它们在设计理念、功能、性能和使用场景上有一些显著的区别。 以下是对这三者的详细比较和介绍。 1. MySQL 概述 类型&#xff1a;关系型数据库管理系统&#xff08;RDBMS&#xff09;。开发者&#xff1a;最…...

Android车机DIY开发之软件篇(十七) Android模拟器移植Automotive

AndroidProducts.mk 路径&#xff1a; /device/generic/goldfish/pc/AndroidProducts.mk sdk_pc_x86_64.mk路径&#xff1a; /device/generic/goldfish/pc/sdk_pc_x86_64.mk sdk_car_x86_64.mk路径&#xff1a; /device/generic/goldfish/car/sdk_car_x86_64.mk BoardConfig.mk…...

[Unity角色控制专题] (借助ai)详细解析官方第三人称控制器

首先模板链接在这里&#xff0c;你可以直接下载并导入unity即可查看官方为开发者写好一套控制器 本文的ai工具用到了豆包&#xff0c;其灵活程度很高&#xff0c;总结能力也强过我太多 因此大量使用&#xff0c;不喜勿喷 Starter Assets - ThirdPerson | Updates in new Charac…...

【数据结构基础_链表】

1、链表的定义 链表与数组的区分&#xff1a; 数组是一块连续的内存空间&#xff0c;有了这块内存空间的首地址&#xff0c;就能直接通过索引计算出任意位置的元素地址。 数组最大的优势是支持通过索引快速访问元素&#xff0c;而链表就不支持。链表不一样&#xff0c;一条链…...

Java 实现 Redis中的GEO数据结构

Java 实现 Redis中的GEO数据结构 LBS &#xff08;基于位置信息服务&#xff08;Location-Based Service&#xff0c;LBS&#xff09;&#xff09;应用访问的数据是和人 或物关联的一组经纬度信息&#xff0c;而且要能查询相邻的经纬度范围&#xff0c;GEO 就非常适合应用在 …...

PostgreSQL如何关闭自动commit

PostgreSQL如何关闭自动commit 在 PostgreSQL 中&#xff0c;默认情况下&#xff0c;每个 SQL 语句都会自动提交&#xff08;即 AUTOCOMMIT 是开启的&#xff09;。如果希望关闭自动提交&#xff0c;以便手动控制事务的提交和回滚&#xff0c;可以通过以下方法实现。 1 使用 …...

1、云原生写在前面

云原生技术是什么&#xff08;包含哪些组件&#xff09;&#xff1f;每个组件是负责什么&#xff1f;学习这些组件技术能解决什问题&#xff1f;哪些类企业需要用到&#xff1f; 这是标准系列的问题&#xff0c;通过 deepseek 的深度思考就能得到我们想要的易于理解的人话式的…...

Redis离线安装

Linux系统Centos安装部署Redis缓存插件 参考&#xff1a;Redis中文网&#xff1a; https://www.redis.net.cn/ 参考&#xff1a;RPM软件包下载地址&#xff1a; https://rpmfind.net/linux/RPM/index.html http://rpm.pbone.net/ https://mirrors.aliyun.com/centos/7/os…...

网络安全-攻击流程-应用层

应用层攻击针对OSI模型的第七层&#xff08;应用层&#xff09;&#xff0c;主要利用协议漏洞、业务逻辑缺陷或用户交互弱点&#xff0c;直接威胁Web应用、API、数据库等服务。以下是常见应用层攻击类型及其流程&#xff0c;以及防御措施&#xff1a; 1. SQL注入&#xff08;SQ…...

java八股文-spring

目录 1. spring基础 1.1 什么是Spring&#xff1f; 1.2 Spring有哪些优点&#xff1f; 1.3 Spring主要模块 1.4 Spring常用注解 1.5 Spring中Bean的作用域 1.6 Spring自动装配的方式 1.7 SpringBean的生命周期 1.8 多级缓存 1.9 循环依赖&#xff1f; 1 .8.1 原因 1.8…...

Jvascript网页设计案例:通过js实现一款密码强度检测,适用于等保测评整改

本文目录 前言功能预览样式特点总结&#xff1a;1. 整体视觉风格2. 密码输入框设计3. 强度指示条4. 结果文本与原因说明 功能特点总结&#xff1a;1. 密码强度检测2. 实时反馈机制3. 详细原因说明4. 视觉提示5. 交互体验优化 密码强度检测逻辑Html代码Javascript代码 前言 能满…...

【Scrapy】Scrapy教程2——工作原理

文章目录 数据流组件引擎Engine调度器Scheduler下载器Downloader爬虫Spiders项目管道Item Pipeline下载器中间件Downloader Middlewares爬虫中间件Spider Middlewares 在学习Scrapy前&#xff0c;我们需要先了解其架构和工作原理&#xff0c;这样才能很好的去使用Scrapy。 Scra…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...