You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Cloud Gateway:如何处理后端主机名/证书不匹配问题?

Spring Cloud Gateway:如何处理后端主机名/证书不匹配问题?

嗨,看了你的配置,我之前确实碰到过几乎一模一样的场景——你的路由指向lb://foo,后端两个ALB的证书都是foo.example.com,但Gateway请求ALB时用的是它们自身的域名(比如x.us-east-1.elb.amazonaws.com),导致SSL校验时域名不匹配抛出异常。结合Spring Cloud Gateway + LoadBalancer的架构,给你两个实操方案,还有对应的注意事项:

方案一:修改请求的SNI信息(推荐,安全合规)

你的核心问题是:Gateway请求ALB时,SNI(服务器名称指示)字段带的是ALB的域名,但ALB返回的证书是foo.example.com,导致域名校验失败。解决思路就是让Gateway发起后端请求时,把SNI设置成和证书一致的foo.example.com

在Spring Cloud Gateway里,你可以通过自定义Reactor Netty HttpClient的配置来实现这个逻辑:

  1. 创建配置类,自定义HttpClient并添加SNI修改逻辑:
import org.springframework.cloud.gateway.config.HttpClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.SslProvider;

import javax.net.ssl.SSLParameters;

@Configuration
public class GatewayHttpClientConfig {

    @Bean
    public HttpClientCustomizer sniCustomizer() {
        return httpClient -> httpClient.secure(sslSpec -> {
            sslSpec.sslContext(SslProvider.DefaultSslContext.from().build())
                   .sslParametersSupplier(() -> {
                       SSLParameters sslParams = new SSLParameters();
                       // 将SNI设置为证书对应的域名foo.example.com
                       sslParams.setServerNames(java.util.List.of(new javax.net.ssl.SNIServerName(
                           javax.net.ssl.SNIServerName.TYPE_HOST_NAME, 
                           "foo.example.com".getBytes()
                       )));
                       return sslParams;
                   });
        });
    }
}
  1. 如果你的场景中有多个路由对应不同的证书域名,还可以结合GatewayFilter实现动态SNI:
    比如针对foo路由,在Filter里提取请求的Host头(你的路由已经匹配了foo.example.com的Host),然后把这个Host设置为SNI。这种方式更灵活,适合多路由多证书的场景。

方案二:跳过主机名验证(仅测试环境使用,风险极高)

你提到的自定义TrustManager跳过主机名校验,确实能快速解决问题,但生产环境绝对不能用——这等于放弃了SSL证书的域名校验,会让你的请求暴露在中间人攻击的风险里。如果只是测试环境临时验证功能,可以这么做:

同样是自定义HttpClient,关闭主机名验证:

import org.springframework.cloud.gateway.config.HttpClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.SslProvider;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

@Configuration
public class InsecureGatewayHttpClientConfig {

    @Bean
    public HttpClientCustomizer insecureSniCustomizer() {
        return httpClient -> {
            try {
                // 创建信任所有证书的TrustManager
                TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {}
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {}
                    }
                };

                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

                return httpClient.secure(sslSpec -> {
                    sslSpec.sslContext(SslProvider.builder().sslContext(sslContext).build())
                           .defaultConfiguration(SslProvider.DefaultConfigurationType.TCP)
                           .handlerConfigurator(sslHandler -> {
                               sslHandler.engine().setSSLParameters(new SSLParameters());
                               // 强制关闭主机名验证
                               sslHandler.engine().setHostnameVerifier((hostname, session) -> true);
                           });
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

再次强调:这个方案仅适合本地测试或内部隔离环境,生产环境严禁使用!

额外小提示

你的ALB同时作为CloudFront源站没问题,本质是因为CloudFront请求源站时,会自动把SNI设置成foo.example.com,和我们方案一的逻辑完全一致。所以Gateway这边只要对齐这个逻辑,就能完美解决证书不匹配的问题。

内容来源于stack exchange

火山引擎 最新活动