You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Integration中完全绕过‘No subject alternative names present’证书校验错误?

解决Spring Integration TCP SSL连接的"No subject alternative names present"错误

你的问题很典型——你自定义的CustomTCPSSLContextSupport只解决了信任任意SSL证书的问题,但No subject alternative names present错误是在主机名验证阶段触发的。JDK在SSL握手完成后,会检查证书中的主体备用名称(SAN)是否包含当前连接的主机名,不匹配就会抛出这个错误,所以只替换TrustManager是不够的,还需要处理主机名验证逻辑。

为什么原有代码不生效?

DefaultTcpSSLContextSupport的getSSLContext方法负责创建信任管理器,但主机名验证是由HostnameVerifier单独处理的,这是SSL连接流程中独立的一步。你的代码跳过了证书信任校验,但没跳过主机名和SAN的匹配校验,所以错误依然存在。

解决方案:同时禁用证书信任校验和主机名校验

下面提供两种可行的实现方式,你可以根据自己的配置风格选择:

方式一:直接给TCP客户端连接工厂设置自定义HostnameVerifier

如果你使用的是Spring Integration默认的TcpNetClientConnectionFactory,可以直接通过配置注入一个信任所有主机名的验证器:

XML配置示例

先定义一个自定义的HostnameVerifier Bean,再注入到TCP连接工厂:

<!-- 自定义信任所有主机名的验证器 -->
<bean id="trustAllHostsVerifier" class="com.yourpackage.TrustAllHostnameVerifier"/>

<bean id="tcpClientConnectionFactory" class="org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory">
    <constructor-arg value="your-server-host"/> <!-- 替换为你的服务端主机名 -->
    <constructor-arg value="443"/> <!-- 替换为你的SSL端口 -->
    <property name="sslContextSupport" ref="sslContextSupport"/>
    <property name="hostnameVerifier" ref="trustAllHostsVerifier"/>
</bean>

对应的自定义HostnameVerifier实现:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

public class TrustAllHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        // 直接返回true,跳过所有主机名与SAN的匹配校验
        return true;
    }
}

Java配置示例

如果用Java Config,代码会更简洁:

@Bean
public TcpNetClientConnectionFactory tcpClientConnectionFactory() throws Exception {
    TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory("your-server-host", 443);
    // 注入你已有的CustomTCPSSLContextSupport
    factory.setSslContextSupport(new CustomTCPSSLContextSupport(
        "${keystore.path}", "${cacerts.path}", "changeit", "changeit"));
    // 直接设置匿名内部类作为主机名验证器
    factory.setHostnameVerifier((hostname, session) -> true);
    return factory;
}

方式二:自定义TcpSocketFactorySupport(适用于复杂场景)

如果需要更灵活地控制SSLSocket的创建过程,可以自定义TcpSocketFactorySupport,在创建Socket时直接设置HostnameVerifier:

import org.springframework.integration.ip.tcp.connection.DefaultTcpSocketFactorySupport;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class CustomTcpSocketFactorySupport extends DefaultTcpSocketFactorySupport {

    private final HostnameVerifier hostnameVerifier;

    public CustomTcpSocketFactorySupport(HostnameVerifier hostnameVerifier) {
        this.hostnameVerifier = hostnameVerifier;
    }

    @Override
    public Socket createSocket(SSLSocketFactory factory, String host, int port) throws IOException, UnknownHostException {
        SSLSocket sslSocket = (SSLSocket) factory.createSocket(host, port);
        // 给Socket绑定自定义的主机名验证器
        sslSocket.setHostnameVerifier(hostnameVerifier);
        return sslSocket;
    }
}

然后在连接工厂中配置这个SocketFactorySupport:

@Bean
public TcpNetClientConnectionFactory tcpClientConnectionFactory() throws Exception {
    TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory("your-server-host", 443);
    factory.setSslContextSupport(new CustomTCPSSLContextSupport(
        "${keystore.path}", "${cacerts.path}", "changeit", "changeit"));
    // 注入自定义的SocketFactorySupport,同时传入信任所有主机的验证器
    factory.setTcpSocketFactorySupport(new CustomTcpSocketFactorySupport((hostname, session) -> true));
    return factory;
}

注意事项

  • 这种绕过校验的方式只适合测试环境,生产环境强烈建议使用合法的、包含正确SAN的SSL证书,避免安全风险。
  • 如果你使用的是TcpNioClientConnectionFactory,配置方式和TcpNetClientConnectionFactory基本一致,同样支持设置hostnameVerifier属性。

内容的提问来源于stack exchange,提问作者İlkay Gunel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:53:31