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

news2025/1/11 17:12:08

背景

一般来说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;

    @Bean
    public 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: 9021
  tomcat:
    min-spare-threads: 400
    max-threads: 800

additional-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;

    @Autowired
    private HttpsServiceConfig httpsServiceConfig;

    public HttpsLoadBalancerFilter(LoadBalancerClient loadBalancer) {
        this.loadBalancer = loadBalancer;
    }

    @Override
    public int getOrder() {
        return FilterEnum.HTTPS_LOAD_BALANCER_FILTER.getCode();
    }
    
    @Override
    public 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: true
        connect-timeout: 10000
        response-timeout: 120000
        pool:
          max-idle-time: 15000
          max-life-time: 45000
          evictionInterval: 5000
      routes:
        - id: bw-star-favorite
          uri: lb://bw-star-favorite
          order: -1
          predicates:
            - Path=/star-favoritear/v1/**
			
bw:
  gateway:
    xssRequestFilterEnable: false
    xssResponseFilterEnable: false
    httpsServices:
      - 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路由切换,从而保障整个割接风险可控和平滑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1969050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WebGoC题解(16) 386.山洞

题目描述 小C和弟弟一起参观影视城&#xff0c;发现里面有m个圆形排列的“小山洞”&#xff0c;山洞用半径20、颜色号为编号的圆表示&#xff0c;由边长是100的线连接&#xff0c;均匀的发布一圈&#xff08;1号洞在正上方&#xff09;。m为8时如下图&#xff1a; 小C跟弟弟提议…

点赞10W+,销售额上百万,如何搭上奥运的流量快车?

2024巴黎奥运会正如火如荼进行中。一边是紧张的赛事与各种抓马事情&#xff0c;一边是法国的浪漫与中国00后的松弛&#xff0c;这届奥运会的看客仿佛瓜田里的猹&#xff0c;忙得不可开交。 作为全球范围内的重大体育盛事&#xff0c;奥运会一直以来都备受公众的热烈关注。 据腾…

Python PDF处理技巧 - 加密和解密PDF

目录 安装Python PDF库 使用Python加密PDF 使用Python设置PDF的安全权限 使用Python解密PDF PDF文档在我们的个人生活和工作中扮演着重要角色。从机密的商业报告到敏感的个人记录&#xff0c;PDF广泛应用于存储和共享重要信息。然而&#xff0c;随着数据泄露和未经授权访问…

facebook脸书登入协议分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;若有侵权&#xff0c;请添加&#xff08;wx&#xff1a;wyqlxl99&#xff09;联系删除 前言: 前段…

MuRF代码阅读

对图像Size的处理, 以适应Transformer 在MVSPlat 当中使用 Center_Crop 裁剪图像&#xff0c;适用于 Transformer 的32 倍数&#xff0c; 其中 焦距 f 不变化&#xff0c;只改变 cx,cy.MuRF 直接对图像进行 插值&#xff0c;合成理想的 size. 根据 ori_size 和 inference_size…

恒驰快讯丨恒驰受邀出席华为云数据库专题销售伙伴思享会

7月25日下午&#xff0c;华为云数据库专题销售伙伴思享会在上海成功落幕。 本次华为云数据库专题销售伙伴思享会&#xff0c;汇集了优秀伙伴、华为云产品专家70人&#xff0c;共同见证了一场关于数据库前沿技术、市场趋势和应用实践的深入交流盛会。上海恒驰信息系统有限公司&a…

【Linux网络编程】套接字Socket

网络编程基础概念&#xff1a; ip地址和端口号 ip地址是网络协议地址&#xff08;4字节32位&#xff0c;形式&#xff1a;xxx.xxx.xxx.xxx xxx在范围[0, 255]内&#xff09;&#xff0c;是IP协议提供的一种统一的地址格式&#xff0c;每台主机的ip地址不同&#xff0c;一个…

【赛事】2024第五届“华数杯”全国大学生数学建模竞赛

为了培养学生的创新意识及运用数学方法和计算机技术解决实际问题的能力&#xff0c;天津市未来与预测科学研究会、中国未来研究会大数据与数学模型专业委员会&#xff08;协办&#xff09;决定举办华数杯全国大学生数学建模竞赛。竞赛的目标是为培养大学生的科学精神及运用数学…

精准识别,无缝转换:2024年顶尖语音转文字技术展示

高效、准确的信息传递与处理成为了我们工作与生活中的重要需求。随着科技的飞速发展&#xff0c;一项革命性的技术——语音转文字软件应运而生。掌握这项技术就能提高工作效率创造更多价值。 1.365在线转文字 链接传送&#xff1a;www.pdf365.cn/ 这个方式虽然不是软件&am…

《四季之歌:春溪潺潺、夏林舞动、秋山守望、冬夜温光》

在这个喧嚣的世界里&#xff0c;有时我们会渴望逃离&#xff0c;去寻找那些安静而纯净的自然角落。每个季节都有它独特的魅力&#xff0c;它们像是大自然精心谱写的交响乐章&#xff0c;我们只需细心聆听&#xff0c;就能感受到四季变换带来的无尽奇境。 春天的序曲&#xff0c…

政企互动 | 南京市雨花台区委副书记王金玉一行莅临聚铭网络视察指导

7月30日&#xff0c;南京市雨花台区委副书记王金玉一行领导莅临聚铭网络进行实地考察与指导。聚铭网络总经理唐开达先生热情接待&#xff0c;双方就聚铭网络的发展现状、业务规模及未来规划展开了深入而愉快的交流。 在唐开达总经理的悉心陪同下&#xff0c;王书记一行深入聚铭…

前后端完全分离实现登录和退出

前后端分离的整合 使用springsecurity前端项目redis完成认证授权的代码 1. 搭建一个前端工程 使用 vue ui搭建&#xff0c;使用webstrom操作 2. 创建一个登录页面 <template><div class"login_container"><!-- 登录盒子 --><div class"l…

深度学习中五种归一化原理

调用库API实现以及手写实现 import torch import torch.nn as nnbatch_szie2 #sample time_steps3 embedding_dim4 #channel num_group2inputxtorch.randn(batch_szie,time_steps,embedding_dim)# N*L*C 批归一化 BatchNorm1d ## 1. 批归一化 实现batch_norm并验证API --…

2024年钉钉杯大学生大数据挑战赛倒计时,最后冲刺

2024第三届钉钉杯大学生大数据挑战赛倒计时&#xff0c;小编给大家带来非常实用的最后冲刺助力【A题】&#xff0c;&#xff08;看图资料预览&#xff09;&#xff1a; 中国烟草行业作为国家税收和财政收入的重要支柱&#xff0c;近年来销售收入持续增长。国家对此实行严格的专…

C++跳跃表个人理解

一、概念 跳跃表是一种基于有序链表的扩展&#xff0c;简称跳表&#xff0c;其就是使用关键节点作为索引的一种数据结构。 可以通过以下方式更快查找到有序链表的某一节点&#xff1a; 利用类似索引的思想&#xff0c;提取出链表中的部分关键节点。比如&#xff0c;给定一个…

C语言 ——— 指针笔试题(中篇)

指针加减整数和解引用的笔试题 笔试题1&#xff1a; int a[5][5]; int(*p)[4]; p a;printf("%p %d", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);问&#xff1a;打印的结果为&#xff1f;&#xff08;分别以 %d 和 %p 的形式打印&#xff09; …

十六、maven git-快速上手(智慧云教育平台)

&#x1f33b;&#x1f33b; 目录 一、概述及项目管理工具介绍1.1 项目介绍1.2 maven 介绍及其配置1.2.1 maven 介绍1.2.2 maven 下载与配置 1.3 pom 中常见标签的使用1.4 后端项目环境的搭建1.5 Git 简介1.6 Git 的基本使用1.6.1 码云的注册与仓库创建1.6.2 上传代码到码云仓库…

【2024】InfluxDB v2 介绍和安装使用(1)

目录&#x1f4bb; 一、介绍1、时序数据库介绍特点&#xff1a;常见的时序数据库时序库受欢迎度排名 2、InfluxDB介绍**InfluxDB 1.x 和 2.0 的主要区别**InfluxDB行协议 二、docker安装 InfluxDB v2三. Web UI常用功能介绍以及使用1、页面介绍2、功能使用2.1、创建bucket2.2、…

如何设计一个测试用例

前言&#x1f440;~ 上一章我们介绍了什么是软件测试以及软件测试的一些基础概念&#xff0c;今天来聊聊如何设计一个测试用例&#xff0c;涉及到黑盒测试的测试方法 基于需求进行测试用例的设计 基于需求的具体设计方法 等价类 边界值 判定表法 正交表法 场景设计法 …

智能ai对话软件都有哪些?分享4款!

在科技日新月异的今天&#xff0c;智能AI对话软件正以前所未有的速度改变着我们的生活方式与工作模式。它们不仅仅是冷冰冰的代码堆砌&#xff0c;更是拥有理解、学习乃至创造能力的智能伙伴&#xff0c;为人类社会带来了前所未有的便捷与乐趣。那么&#xff0c;究竟有哪些智能…