最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot服務(wù)HTTP/HTTPS雙監(jiān)聽(tīng)及路由的實(shí)現(xiàn)示例

 更新時(shí)間:2025年10月21日 10:46:06   作者:BossFriday  
本文主要介紹了通過(guò)SpringBoot中配置額外的HTTPS監(jiān)聽(tīng)端口和在SpringCloudGateway中添加自定義配置和過(guò)濾器來(lái)重寫(xiě)URI實(shí)現(xiàn)HTTPS路由,這樣的設(shè)計(jì)旨在減少生產(chǎn)環(huán)境中切換到HTTPS時(shí)的困難和風(fēng)險(xiǎn)

背景

一般來(lái)說(shuō)SpringCloud Gateway到后面服務(wù)的路由屬于內(nèi)網(wǎng)交互,因此路由方式是否是Https就顯得不是那么重要了。事實(shí)上也確實(shí)如此,大多數(shù)的應(yīng)用開(kāi)發(fā)時(shí)基本都是直接Http就過(guò)去了,不會(huì)一開(kāi)始就是直接上Https。然而隨著時(shí)間的推移,項(xiàng)目規(guī)模的不斷擴(kuò)大,當(dāng)被要求一定要走Https時(shí),就會(huì)面臨一種困惑:將所有服務(wù)用一刀切的方式改為Https方式監(jiān)聽(tīng),同時(shí)還要將網(wǎng)關(guān)服務(wù)所有的路由方式也全部切為Https方式,一旦生產(chǎn)環(huán)境上線(xiàn)出問(wèn)題將要面臨全量服務(wù)的歸滾,這時(shí)運(yùn)維很可能跳出來(lái)說(shuō):生產(chǎn)環(huán)境幾十個(gè)服務(wù),每個(gè)服務(wù)最少2個(gè)節(jié)點(diǎn),全量部署和回滾不可能在短時(shí)間完成。另外測(cè)試同學(xué)也可能會(huì)說(shuō),現(xiàn)在沒(méi)有全量接口自動(dòng)化回歸測(cè)試工具,做一個(gè)次人工的全量接口回歸測(cè)試也不現(xiàn)實(shí)。因此在這種情況下最穩(wěn)妥的方式是實(shí)現(xiàn):SpringCloud Gateway & SpringBoot RestController Http/Https雙支持,這樣可以做到分批分次進(jìn)行切換,那么上面的困惑自然也就不存在了。

1. SpringBoot Http/Https監(jiān)聽(tīng)雙支持

1.1 代碼實(shí)現(xiàn)

為了不對(duì)原來(lái)的Http監(jiān)聽(tīng)產(chǎn)生任何影響,因此需要保障以下兩點(diǎn):
1、原主端口(server.port)監(jiān)聽(tīng)什么都不變,監(jiān)聽(tīng)方式仍為http,附加端口監(jiān)聽(tīng)方式為https。(需要繞開(kāi)的問(wèn)題是:如果一個(gè)服務(wù)有多個(gè)監(jiān)聽(tīng)端口,主端口會(huì)優(yōu)先選擇https方式)
2、附加端口不進(jìn)行nacos服務(wù)注冊(cè)(主要的考慮點(diǎn)還是不對(duì)原來(lái)的http監(jiān)聽(tīng)和路由產(chǎn)生任何影響,這里我的方案是https監(jiān)聽(tīng)端口號(hào)為http端口+10000)。

這樣就能實(shí)現(xiàn)SpringBoot服務(wù)主端口Http監(jiān)聽(tīng),附加端口Https監(jiān)聽(tīng)。

實(shí)現(xiàn)代碼如下:

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、上述代碼中的配置默認(rèn)值大家自行修改(key-store:XXX.p12,key-store-password:XXX),如果覺(jué)得配置additional-https-connector相關(guān)配置命名不合適也可自行修改。當(dāng)配置好additional-https-connector相關(guān)配置(additional-https-connector.ssl.enabled是一個(gè)https附加端口監(jiān)聽(tīng)的開(kāi)關(guān)),啟動(dòng)服務(wù)就可以看到類(lèi)似如下的日志,同時(shí)查看nacos中的服務(wù)實(shí)例也會(huì)發(fā)現(xiàn)并沒(méi)有進(jìn)行https端口的服務(wù)注冊(cè);
2、這里我用的是p12自簽證書(shū),證書(shū)需要放到項(xiàng)目的resouces目錄下(可以用keytool -genkey命令去生成一個(gè))。

1.2 配置

配置示例如下,keyStore、keyStorePassword、keyStoreType使用代碼中的默認(rèn)值,需要更換證書(shū)的時(shí)候再進(jìn)行配置。

server:
  port: 9021
  tomcat:
    min-spare-threads: 400
    max-threads: 800

additional-https-connector:
  ssl:
    enabled: true

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

思路:在網(wǎng)關(guān)服務(wù)增加自定義配置(HttpsServiceConfig)來(lái)定義需要切換為https路由的服務(wù)列表,然后使用過(guò)濾器(HttpsLoadBalancerFilter)進(jìn)行轉(zhuǎn)發(fā)uri的https重寫(xiě);

這樣就能實(shí)現(xiàn)在配置列表中的服務(wù)進(jìn)行Https路由,否則保持原有Https路由。

2.1 代碼實(shí)現(xiàn)

  • 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、這里實(shí)現(xiàn)了配置的刷新,因此需要進(jìn)行服務(wù)的https路由切換時(shí)只需修改配置即可,而網(wǎng)關(guān)服務(wù)不需要重啟;
2、過(guò)濾器使用Set進(jìn)行判斷,效率上肯定優(yōu)于對(duì)List的遍歷查找;
3、過(guò)濾器的Order建議放到最后,因此可以直接使用Integer.MAX_VALUE(我們的項(xiàng)目中有多個(gè)過(guò)濾器,并且通過(guò)FilterEnum枚舉去統(tǒng)一管理);

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、需要變更的配置為:

  • 開(kāi)啟ssl信任(spring.cloud.gateway.httpclient.ssl):
  • 設(shè)置https路由服務(wù)列表(bw.gateway.httpsServices)

結(jié)束語(yǔ)

通過(guò)上述兩步就能實(shí)現(xiàn)SpringCloud Gateway & SpringBoot RestController Http/Https雙支持,嚴(yán)謹(jǐn)?shù)淖龇ㄊ沁€需要將FeignClient的調(diào)用進(jìn)行Https化,上面的實(shí)現(xiàn)方式中之所以不對(duì)https端口進(jìn)行注冊(cè)的原因就是避免Http方式的FeignClient去調(diào)用Https目標(biāo)端口從而引發(fā)問(wèn)題。關(guān)于FeignClient的Https切換實(shí)際上也可以借鑒網(wǎng)關(guān)的思路將請(qǐng)求uri重寫(xiě)為端口號(hào)+10000的https請(qǐng)求即可。

那么通過(guò)這個(gè)思路就可以實(shí)現(xiàn):服務(wù)的分批、FeignClient分步Https路由切換,從而保障整個(gè)割接風(fēng)險(xiǎn)可控和平滑。

到此這篇關(guān)于Springboot服務(wù)HTTP/HTTPS雙監(jiān)聽(tīng)及路由的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Springboot HTTP/HTTPS雙監(jiān)聽(tīng)及路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中&&與?表達(dá)式結(jié)合時(shí)出現(xiàn)的坑

    Java中&&與?表達(dá)式結(jié)合時(shí)出現(xiàn)的坑

    這篇文章主要給大家介紹了關(guān)于Java中&&與?表達(dá)式結(jié)合時(shí)出現(xiàn)的坑的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • java實(shí)現(xiàn)左旋轉(zhuǎn)字符串

    java實(shí)現(xiàn)左旋轉(zhuǎn)字符串

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)左旋轉(zhuǎn)字符串,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • Java中AuthorizationFilter過(guò)濾器的功能

    Java中AuthorizationFilter過(guò)濾器的功能

    本文主要介紹了Java中AuthorizationFilter過(guò)濾器的功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2026-02-02
  • 一文帶你真正理解Java中的內(nèi)部類(lèi)

    一文帶你真正理解Java中的內(nèi)部類(lèi)

    不知道大家在平時(shí)的開(kāi)發(fā)過(guò)程中或者源碼里是否留意過(guò)內(nèi)部類(lèi),那有思考過(guò)為什么要有內(nèi)部類(lèi),內(nèi)部類(lèi)都有哪幾種形式,本篇文章主要帶領(lǐng)大家理解下這塊內(nèi)容
    2022-08-08
  • spring-boot報(bào)錯(cuò)javax.servlet.http不存在的問(wèn)題解決

    spring-boot報(bào)錯(cuò)javax.servlet.http不存在的問(wèn)題解決

    當(dāng)springboot項(xiàng)目從2.7.x的升級(jí)到3.0.x的時(shí)候,會(huì)遇到j(luò)avax.servlet.http不存在,本文就來(lái)介紹一下這個(gè)問(wèn)題的解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • 通過(guò)springboot發(fā)布WebService接口并調(diào)用方式

    通過(guò)springboot發(fā)布WebService接口并調(diào)用方式

    Spring Boot集成CXF需注意版本對(duì)應(yīng),配置注解并發(fā)布服務(wù),通過(guò)WSDL驗(yàn)證,結(jié)合Controller和Swagger測(cè)試,CXF支持SOAP、REST等服務(wù),提供代碼與合同優(yōu)先開(kāi)發(fā)模式
    2025-09-09
  • java 創(chuàng)建線(xiàn)程的四種方式

    java 創(chuàng)建線(xiàn)程的四種方式

    這篇文章主要介紹了java 創(chuàng)建線(xiàn)程的四種方式,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11
  • Java多線(xiàn)程基本用法總結(jié)

    Java多線(xiàn)程基本用法總結(jié)

    本篇文章主要總結(jié)了Java線(xiàn)程的一些基本的用法。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • Java ThreadLocal類(lèi)應(yīng)用實(shí)戰(zhàn)案例分析

    Java ThreadLocal類(lèi)應(yīng)用實(shí)戰(zhàn)案例分析

    這篇文章主要介紹了Java ThreadLocal類(lèi)應(yīng)用,結(jié)合具體案例形式分析了java ThreadLocal類(lèi)的功能、原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • JDK8中String的intern()方法實(shí)例詳細(xì)解讀

    JDK8中String的intern()方法實(shí)例詳細(xì)解讀

    String字符串在我們?nèi)粘i_(kāi)發(fā)中最常用的,當(dāng)然還有他的兩個(gè)兄弟StringBuilder和StringBuilder,接下來(lái)通過(guò)本文給大家介紹JDK8中String的intern()方法詳細(xì)解讀,需要的朋友可以參考下
    2022-09-09

最新評(píng)論

新巴尔虎左旗| 稻城县| 平潭县| 昭觉县| 大英县| 九台市| 浙江省| 天气| 清苑县| 宾阳县| 乌拉特中旗| 苗栗市| 安顺市| 贡山| 黑河市| 政和县| 三门县| 郓城县| 乌什县| 通城县| 明光市| 兰州市| 玉门市| 西丰县| 尚志市| 常德市| 五指山市| 长兴县| 曲水县| 乌拉特前旗| 岳阳市| 阿克陶县| 曲周县| 尚志市| 内江市| 精河县| 习水县| 广东省| 伊春市| 乐亭县| 康定县|