主页 > 开源代码  > 

【动态路由】系统weburl整合系列【springcloud-gateway实现】【不改hosts文件版】组件一

【动态路由】系统weburl整合系列【springcloud-gateway实现】【不改hosts文件版】组件一
需求

      实现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:// .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 piler.source>17</maven piler.source> <maven piler.target>17</maven piler.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.0 pmd-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 mons</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 piler.target}</target> <source>${maven piler.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:8207 gwproxy.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:30001 gwproxy.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:9000 gwproxy.routes.ctapisix_gz_gzb1_prod=http://10.***.5.131:32065 gwproxy.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:30006 gwproxy.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:30006 gwproxy.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:8082 gwproxy.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; @Autowired private NacosMappingConfig nacosMappingConfig; @Autowired private CmdbFilterFactory1 cmdbFilterFactory1; @Autowired private CmdbFilterFactory2 cmdbFilterFactory2; @Autowired private ApisixFilterFactory1 apisixFilterFactory1; @Autowired private ApisixFilterFactory2 apisixFilterFactory2; @Autowired private EurekaFilterFactory1 eurekaFilterFactory1; @Autowired private EurekaFilterFactory2 eurekaFilterFactory2; @Autowired private N9eFilterFactory1 n9eFilterFactory1; @Autowired private N9eFilterFactory2 n9eFilterFactory2; // @Autowired // private KibanaFilterFactory1 kibanaFilterFactory1; // @Autowired // private KibanaFilterFactory2 kibanaFilterFactory2; @Autowired private TimeTaskFilterFactory1 timeTaskFilterFactory1; @Autowired private TimeTaskFilterFactory2 timeTaskFilterFactory2; @Bean // @RefreshScope public 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、相互之间不能是包含(子集)关系 * * */ // 先统一处理所有eureka while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); if(entry.getKey().contains("eureka")){ innerBuilder = EurekaRoute.route(eurekaFilterFactory1, eurekaFilterFactory2, innerBuilder, entry, rootUri); } // 防止不重复处理 用 else if else 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前端工程( github /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); } // @todo else if(entry.getKey().contains("grafana")){ // /grafana_sh_prod/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } else if(entry.getKey().contains("ambari")){ // /ambari_sh_prod/#/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // eureka √ else if(entry.getKey().contains("kafkamap")){ // /kafkamap_sh_jg92_test/#/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // cmak 存在类似n9e的菜单路径问题 else if(entry.getKey().contains("cmak")){ // /cmak_sh_jg93_test innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 补充了一个js待验证 else if(entry.getKey().contains("skywalking")){ // /skywalking_sh_jg131_test/General-Service/Services innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 初步让为 √ else if(entry.getKey().contains("pinpoint")){ // /pinpoint_gcgz_gzb1_prod/#/main innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 120网络不通 else if(entry.getKey().contains("redmine")){ // /redmine_gcgz_nmb3_prod/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 初步让为harbor上下文路径可以修改 else if(entry.getKey().contains("harbor")){ // /harbor_gcgz_gzb1_prod/harbor/sign-in innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 能登陆,但主页面显示不正常 else if(entry.getKey().contains("jenkins")){ // /jenkins_sh_cs124_prod/jenkins/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 登陆有问题 else if(entry.getKey().contains("nexus")){ // /nexus_sh_jg122_prod/nexus/#welcome innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 静态资源访问有问题 else if(entry.getKey().contains("gitlab")){ // /gitlab_sh_jg122_prod/users/sign_in innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 未知错误 else if(entry.getKey().contains("kubesphere")){ // /kubesphere_sh_cs131_prod/login innerBuilder = CommonRoute.route( innerBuilder, entry, rootUri); } // 可以登陆,静态资源加载有问题 else if(entry.getKey().contains("opsportal")){ // /opsportal_sh_prod/login innerBuilder = 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", // id r -> 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要配两个路由,第二个 modifyRedirectFilterFactory innerBuilder.route( entry.getKey()+"_2", // id r -> r.header("Referer",rootUri+"/"+entry.getKey()+"[^\\s]*" ) // .filters( f -> f.stripPrefix(0).preserveHostHeader() ) .filters(new Function<GatewayFilterSpec, UriSpec>() { @Override public 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 */ @Override public 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); } } })); } @Override public Class<NameValueConfig> getConfigClass() { // 如果你有配置属性,返回配置属性的类;如果没有,返回null return 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 */ @Override public 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\") "); } } })); } @Override public Class<NameValueConfig> getConfigClass() { // 如果你有配置属性,返回配置属性的类;如果没有,返回null return NameValueConfig.class; // 或者 return YourConfigProperties.class; } } 效果说明

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

server: port: 8089 gwproxy: root_uri: ${root_uri:http://127.0.0.1:8089} spring: application: name: api-gateway cloud: gateway: httpclient: response-timeout: PT60S connect-timeout: 2000 # 链接池配置 pool: # 最大连接数 max-connections: 10000 # 仅对于FIXED类型,等待获取线程的最长时间(毫秒) acquire-timeout: 1000 # 最大空闲时间 max-idle-time: PT10S # 设置固定链接池 type: fixed discovery: locator: enabled: true lower-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_2 uri: http://eureka:eureka@127.0.0.1:8200 #匹配后提供服务的路由地址 predicates: - Header=Referer,${gwproxy.root_uri}/cteureka management: info: # 显示任意的应用信息,默认关闭 springBoot版本:2.7.15 GA如果是<2.7.5 的版本默认是开启的 env: enabled: true endpoints: web: exposure: include: "*" base-path: /whoami info: app: name: gateway encoding: "@project.build.sourceEncoding@" java: source: "@java.version@" target: "@java.version@" company: name: .asiainfo debug: true 附件一:整合界面地址效果截图

附件二:springcloud gateway官方文档

Spring Cloud Gateway 帮助文档

Spring Cloud Gateway 官方首页

标签:

【动态路由】系统weburl整合系列【springcloud-gateway实现】【不改hosts文件版】组件一由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【动态路由】系统weburl整合系列【springcloud-gateway实现】【不改hosts文件版】组件一