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

SpringCloud网关 SpringBoot服务 HTTP/HTTPS路由/监听双支持

背景

一般来说SpringCloud Gateway到后面服务的路由属于内网交互,因此路由方式是否是Https就显得不是那么重要了。事实上也确实如此,大多数的应用开发时基本都是直接Http就过去了,不会一开始就是直接上Https。然而随着时间的推移,项目规模的不断扩大,当被要求一定要走Https时,就会面临一种困惑:将所有服务用一刀切的方式改为Https方式监听,同时还要将网关服务所有的路由方式也全部切为Https方式,一旦生产环境上线出问题将要面临全量服务的归滚,这时运维很可能跳出来说:生产环境几十个服务,每个服务最少2个节点,全量部署和回滚不可能在短时间完成。另外测试同学也可能会说,现在没有全量接口自动化回归测试工具,做一个次人工的全量接口回归测试也不现实。因此在这种情况下最稳妥的方式是实现:SpringCloud Gateway & SpringBoot RestController Http/Https双支持,这样可以做到分批分次进行切换,那么上面的困惑自然也就不存在了。

1. SpringBoot Http/Https监听双支持

1.1 代码实现

为了不对原来的Http监听产生任何影响,因此需要保障以下两点:
1、原主端口(server.port)监听什么都不变,监听方式仍为http,附加端口监听方式为https。(需要绕开的问题是:如果一个服务有多个监听端口,主端口会优先选择https方式)
2、附加端口不进行nacos服务注册(主要的考虑点还是不对原来的http监听和路由产生任何影响,这里我的方案是https监听端口号为http端口+10000)。

这样就能实现SpringBoot服务主端口Http监听,附加端口Https监听。

实现代码如下:

import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** HttpsConnectorAddInConfiguration** @author chenx*/
@Configuration
public class HttpsConnectorAddInConfiguration {private static final int HTTPS_PORT_OFFSET = 10000;@Value("${server.port}")private int port;@Value("${additional-https-connector.ssl.key-store:XXX.p12}")private String keyStore;@Value("${additional-https-connector.ssl.key-store-password:XXX}")private String keyStorePassword;@Value("${additional-https-connector.ssl.key-store-type:PKCS12}")private String keyStoreType;@Value("${additional-https-connector.ssl.enabled:false}")private boolean enabled;@Beanpublic WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() {return server -> {if (!this.enabled) {return;}Connector httpsConnector = this.createHttpsConnector();server.addAdditionalTomcatConnectors(httpsConnector);};}/*** createHttpsConnector** @return*/private Connector createHttpsConnector() {Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);connector.setScheme("https");connector.setPort(this.port + HTTPS_PORT_OFFSET);connector.setSecure(true);Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();protocol.setSSLEnabled(true);protocol.setKeystoreFile(this.keyStore);protocol.setKeystorePass(this.keyStorePassword);protocol.setKeystoreType(this.keyStoreType);protocol.setSslProtocol("TLS");return connector;}
}

备注:
1、上述代码中的配置默认值大家自行修改(key-store:XXX.p12,key-store-password:XXX),如果觉得配置additional-https-connector相关配置命名不合适也可自行修改。当配置好additional-https-connector相关配置(additional-https-connector.ssl.enabled是一个https附加端口监听的开关),启动服务就可以看到类似如下的日志,同时查看nacos中的服务实例也会发现并没有进行https端口的服务注册;
2、这里我用的是p12自签证书,证书需要放到项目的resouces目录下(可以用keytool -genkey命令去生成一个)。
在这里插入图片描述

1.2 配置

配置示例如下,keyStore、keyStorePassword、keyStoreType使用代码中的默认值,需要更换证书的时候再进行配置。

server:port: 9021tomcat:min-spare-threads: 400max-threads: 800additional-https-connector:ssl:enabled: true

2. SpringCloud Gateway Http/Https路由双支持

思路:在网关服务增加自定义配置(HttpsServiceConfig)来定义需要切换为https路由的服务列表,然后使用过滤器(HttpsLoadBalancerFilter)进行转发uri的https重写;

这样就能实现在配置列表中的服务进行Https路由,否则保持原有Https路由。

2.1 代码实现

  • HttpsServiceConfig
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;import java.util.HashSet;
import java.util.List;
import java.util.Set;/*** HttpsServiceConfig** @author chenx*/
@Slf4j
@Component
@RefreshScope
@ConfigurationProperties(prefix = "bw.gateway")
public class HttpsServiceConfig {private List<String> httpsServices;private Set<String> httpsServiceSet = new HashSet<>();public List<String> getHttpsServices() {return this.httpsServices;}public void setHttpsServices(List<String> httpsServices) {this.httpsServices = httpsServices;this.updateHttpsServices();}public Set<String> getHttpsServiceSet() {return this.httpsServiceSet;}/*** handleRefreshEvent*/@EventListener(RefreshEvent.class)public void handleRefreshEvent() {this.updateHttpsServices();}/*** updateHttpsServices*/private void updateHttpsServices() {this.httpsServiceSet = CollectionUtils.isNotEmpty(this.httpsServices) ? new HashSet<>(this.httpsServices) : new HashSet<>();log.info("httpsServiceSet updated, httpsServiceSet.size() = {}", this.httpsServiceSet.size());}
}
  • HttpsLoadBalancerFilter
import com.beam.work.gateway.common.FilterEnum;
import com.beam.work.gateway.config.HttpsServiceConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;import java.net.URI;
import java.util.Objects;/*** HttpsLoadBalancerFilter** @author chenx*/
@Slf4j
@RefreshScope
@Component
public class HttpsLoadBalancerFilter implements GlobalFilter, Ordered {private static final int HTTPS_PORT_OFFSET = 10000;private final LoadBalancerClient loadBalancer;@Autowiredprivate HttpsServiceConfig httpsServiceConfig;public HttpsLoadBalancerFilter(LoadBalancerClient loadBalancer) {this.loadBalancer = loadBalancer;}@Overridepublic int getOrder() {return FilterEnum.HTTPS_LOAD_BALANCER_FILTER.getCode();}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);boolean isRewriteToHttps = Objects.nonNull(route) && this.httpsServiceConfig.getHttpsServiceSet().contains(route.getId());if (isRewriteToHttps) {ServiceInstance instance = this.loadBalancer.choose(route.getUri().getHost());if (Objects.nonNull(instance)) {URI originalUri = exchange.getRequest().getURI();URI httpsUri = UriComponentsBuilder.fromUri(originalUri).scheme("https").host(instance.getHost()).port(instance.getPort() + HTTPS_PORT_OFFSET).build(true).toUri();log.info("HttpsLoadBalancerFilter RewriteToHttps: {}", httpsUri.toString());exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, httpsUri);}}return chain.filter(exchange);}
}

备注:
1、这里实现了配置的刷新,因此需要进行服务的https路由切换时只需修改配置即可,而网关服务不需要重启;
2、过滤器使用Set进行判断,效率上肯定优于对List的遍历查找;
3、过滤器的Order建议放到最后,因此可以直接使用Integer.MAX_VALUE(我们的项目中有多个过滤器,并且通过FilterEnum枚举去统一管理);

2.2 配置

配置示例:

spring:cloud:gateway:enabled: true httpclient:ssl:use-insecure-trust-manager: trueconnect-timeout: 10000response-timeout: 120000pool:max-idle-time: 15000max-life-time: 45000evictionInterval: 5000routes:- id: bw-star-favoriteuri: lb://bw-star-favoriteorder: -1predicates:- Path=/star-favoritear/v1/**bw:gateway:xssRequestFilterEnable: falsexssResponseFilterEnable: falsehttpsServices:- bw-star-favorite

备注:
1、需要变更的配置为:

  • 开启ssl信任(spring.cloud.gateway.httpclient.ssl):
  • 设置https路由服务列表(bw.gateway.httpsServices)
    在这里插入图片描述

结束语

通过上述两步就能实现SpringCloud Gateway & SpringBoot RestController Http/Https双支持,严谨的做法是还需要将FeignClient的调用进行Https化,上面的实现方式中之所以不对https端口进行注册的原因就是避免Http方式的FeignClient去调用Https目标端口从而引发问题。关于FeignClient的Https切换实际上也可以借鉴网关的思路将请求uri重写为端口号+10000的https请求即可。

那么通过这个思路就可以实现:服务的分批、FeignClient分步Https路由切换,从而保障整个割接风险可控和平滑。

相关文章:

SpringCloud网关 SpringBoot服务 HTTP/HTTPS路由/监听双支持

背景 一般来说SpringCloud Gateway到后面服务的路由属于内网交互&#xff0c;因此路由方式是否是Https就显得不是那么重要了。事实上也确实如此&#xff0c;大多数的应用开发时基本都是直接Http就过去了&#xff0c;不会一开始就是直接上Https。然而随着时间的推移&#xff0c…...

JavaScript做网页是否过期的处理

通过路由上的参数生成唯一md5和路由上token做验证_md5 token-CSDN博客 前言&#xff1a;基于这篇文章我们做网页是否超时&#xff0c;网页是否过期的处理。打开一个网页允许他在一定时间内可以访问&#xff0c;过了这个时间就不可以访问了&#xff0c;encrypt是h5加密方法&…...

python coding时遇到的问题

Q&#xff1a;只有cpu的时候加载模型 A&#xff1a;checkpoint torch.load(model_path, map_locationtorch.device(‘cpu’)) Q&#xff1a;vscode的文件路径和spyder的不一样 A&#xff1a;在vscode中&#xff0c;右键要用的文件&#xff0c;选择“文件相对路径”...

攻防演练号角吹响,聚铭铭察高级威胁检测系统助您零失分打赢重保攻坚战

在数字化浪潮中&#xff0c;攻防演练成为了衡量网络安全防御力的核心标尺&#xff0c;其重要性与日俱增。这项由政府、行业监管或企业内部主导的安全活动&#xff0c;随着互联网普及而兴起&#xff0c;现已发展成为全球公认的检验网络安全体系效能的标准。它不仅关乎技术实力的…...

个人量化交易兴起!有什么好用的量化软件推荐?迅投QMT量化平台简介!

QMT是专门为机构、活跃投资者、高净值客户等专业投资者研发的智能量化交易终端&#xff0c;拥有高速行情、极速交易、策略交易、多维度风控等专业功能&#xff0c;满足专业投资者的特殊交易需求。覆盖业务范围广:沪深A股、港股通、两融、期权、期货。 适合用QMT的投资者&#x…...

SQL labs-SQL注入(七,sqlmap对于post传参方式的注入,2)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。参考&#xff1a;SQL注入之Header注入_sqlmap header注入-CSDN博客 序言&#xff1a; 本文主要讲解基于SQL labs靶场&#xff0c;sqlmap工具进行的post传参方式的SQL注入&#xff0c…...

SAM 2: Segment Anything in Images and Videos

Introduction 提出的目的&#xff1a; 1.现有的应用像自动驾驶&#xff0c;AR等来说都是需要temporal localization beyond image-level segmentation&#xff08;时序定位而不仅是图片分割&#xff09; 2. 一个好的分割模型不应该仅仅局限于图片领域&#xff0c;而是图视频两…...

软件测试面试,如何自我介绍?

又是一年金九银十&#xff0c;相信不少小伙伴都在准备跳槽面试&#xff0c;而面试中一个必不可少的环节就是自我介绍&#xff0c;所以&#xff0c;今天我们就来聊一聊软件测试面试中如何自我介绍。 为什么要自我介绍 在讨论如何自我介绍之前&#xff0c;我们先来讨论一下为…...

力扣第四十七题——全排列II

内容介绍 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2],[1,2,1],[2,1,1]]示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],…...

Springer旗下中科院2区TOP,国人优势大!

关注GZH【欧亚科睿学术】&#xff0c;第一时间了解期刊最新动态&#xff01; 1 通信网络类 【期刊简介】IF&#xff1a;4.0-5.0&#xff0c;JCR1区&#xff0c;中科院3区 【出版社】ELSEVIER出版社 【检索情况】SCIE&EI双检&#xff0c;CCF-C类 【征稿领域】通信网络的…...

【C++】C++入门知识详解(下)

大家好~我们接着【C】C入门知识详解&#xff08;上&#xff09;-CSDN博客来介绍另一些C入门基础知识。 1.缺省值和缺省参数 缺省参数就是声明或定义函数时为函数的参数指定一个缺省参数。在调用该函数时&#xff0c;如果没有指定实参&#xff0c;则采用该形参的缺省值&#xf…...

分压电阻方式的ADC电压校准

无人机有个流程是电池电压校准。具体做法是:让你用万用表测量一下电池两端的电压,然后输入到文本框中,电机计算能重新计算出电压分压器的值,从而获得电池电压值。 这种方法实现的原理是这样的: 电阻分压检测电压原理,以上图为例: 当电路确定时,R2/(R1+R2)是一个定值R,…...

使用Postman测试API短轮询机制:深入指南

短轮询是一种Web开发中常用的技术&#xff0c;用于在客户端和服务器之间定期检查更新。与长轮询或WebSockets等技术相比&#xff0c;短轮询简单易实现&#xff0c;但可能带来较多的HTTP请求&#xff0c;从而增加服务器负担。Postman作为一个强大的API测试工具&#xff0c;可以用…...

明清进士人数数据

明清进士人数数据 指标&#xff1a;省份名称、城市名称、区县名称、明清各省进士人数、明清各城市进士人数、明清各县区进士人数 指标说明&#xff1a; Province[省份名称]-统计数据所属省份 City[城市名称]-统计数据所属地级市 Region[区县名称]-统计数据所属区县 MQpro…...

C# 串口通信(通过serialPort控件发送及接收数据)

连接串口 界面设计打开串口发送数据通过文件发送发送数据 接收数据 首先可以在 工具箱中搜索serialport&#xff0c;将控件拖到你的Winfrom窗口。 界面设计 打开串口 private void Connect_Click(object sender, EventArgs e){serialPort1.PortName comboBox2.Text;//端口名s…...

数据安全的新盾牌:SQL Server数据库镜像技术详解

数据安全的新盾牌&#xff1a;SQL Server数据库镜像技术详解 在数据驱动的商业世界中&#xff0c;数据库的安全性是维护企业运营的关键。SQL Server提供了多种数据保护机制&#xff0c;其中数据库镜像技术是一个强大的高可用性解决方案&#xff0c;它可以显著提高数据的安全性…...

【C语言版】数据结构教程(一)绪论(上)

【内容简介】本文整理数据结构&#xff08;C语言版&#xff09;相关内容的复习笔记&#xff0c;供各位朋友借鉴学习。本章内容更偏于记忆和理解&#xff0c;请读者们耐心阅读。 数据结构教程 绪论&#xff08;上&#xff09; 本节学习目标 1.1 基本概念 1.2 抽象数据类型的表示…...

酒后为什么总感觉渴?

喝酒后感到口渴&#xff0c;这种感觉其实很常见。这主要是因为酒精对我们的身体有几种影响。首先&#xff0c;酒精能够扩张血管&#xff0c;这会加快血液循环&#xff0c;让肾脏更加活跃&#xff0c;产生更多的尿液。这样一来&#xff0c;我们体内的水分就会通过排尿流失&#…...

Docker安装OwnCloud私有云盘对接ceph

一、安装OwnCloud 我的安装包链接&#xff1a;https://pan.baidu.com/s/1cJO8WEonsw4gGQWgQaYzpw?pwd6bak 提取码&#xff1a;6bak 启动OwnCloud容器&#xff0c;没有镜像会自动下载 docker run -d -p 80:80 -v /home/owncloud:/var/www/html --name owncloud --restartalway…...

创建了Vue项目,需要导入什么插件以及怎么导入

如果你不知道怎么创建Vue项目,建议可以看一看这篇文章 怎么安装Vue的环境和搭建Vue的项目-CSDN博客 1.在idea中打开目标文件 2.系在一个插件Vue.js 3.下载ELement UI 在Terminal中输入 # 切换到项目根目录 cd vueadmin-vue # 或者直接在idea中执行下面命令 # 安装element-u…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

前端高频面试题2:浏览器/计算机网络

本专栏相关链接 前端高频面试题1&#xff1a;HTML/CSS 前端高频面试题2&#xff1a;浏览器/计算机网络 前端高频面试题3&#xff1a;JavaScript 1.什么是强缓存、协商缓存&#xff1f; 强缓存&#xff1a; 当浏览器请求资源时&#xff0c;首先检查本地缓存是否命中。如果命…...