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

Java8+WildFly10.1环境下jaxws-maven-plugin生成SOAP客户端SNI缺失问题

解决WildFly 10.1中JAX-WS客户端SNI缺失导致的SSLHandshakeException问题

我之前踩过完全一样的坑——JUnit里调用JAX-WS客户端一切正常,部署到WildFly 10.1就抛出SSLHandshakeException: unrecognized_name,核心原因就是WildFly环境下的JAX-WS客户端没有自动发送SNI(Server Name Indication)扩展信息,而目标SSL服务器依赖SNI来识别虚拟主机。下面给你几个亲测有效的解决办法:

方法1:通过JVM参数强制启用SNI扩展

WildFly默认可能关闭了JSSE的SNI扩展(或者继承了自定义的JVM配置),你可以修改WildFly的启动脚本,强制开启SNI:

  • 找到WildFly安装目录下的bin/standalone.conf(独立模式)或bin/domain.conf(域模式)
  • 在JAVA_OPTS中添加参数:
    -Djsse.enableSNIExtension=true
    
  • 重启WildFly后再测试连接

这个方法最简单,不需要修改代码,适合全局启用SNI的场景。

方法2:在代码中手动配置SNI

如果不想修改全局JVM参数,也可以在创建JAX-WS客户端时,自定义SSLSocketFactory来手动设置SNI主机名:

步骤1:实现带SNI支持的SSLSocketFactory

import javax.net.ssl.*;
import java.io.IOException;
import java.net.*;
import java.util.Collections;

public class SNIEnabledSSLSocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory delegateFactory;
    private final String sniHostname;

    public SNIEnabledSSLSocketFactory(SSLSocketFactory delegate, String sniHostname) {
        this.delegateFactory = delegate;
        this.sniHostname = sniHostname;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegateFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegateFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        SSLSocket sslSocket = (SSLSocket) delegateFactory.createSocket(socket, host, port, autoClose);
        configureSNI(sslSocket);
        return sslSocket;
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        SSLSocket sslSocket = (SSLSocket) delegateFactory.createSocket(host, port);
        configureSNI(sslSocket);
        return sslSocket;
    }

    // 其他createSocket方法直接委托给delegateFactory即可
    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return delegateFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return delegateFactory.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return delegateFactory.createSocket(address, port, localAddress, localPort);
    }

    private void configureSNI(SSLSocket sslSocket) {
        if (sniHostname == null || sniHostname.isEmpty()) return;
        SSLParameters params = sslSocket.getSSLParameters();
        SNIServerName sniServerName = new SNIServerName(SNIServerNameType.HOST_NAME, sniHostname.getBytes());
        params.setServerNames(Collections.singletonList(sniServerName));
        sslSocket.setSSLParameters(params);
    }
}

步骤2:在JAX-WS客户端中使用这个SocketFactory

// 初始化你的JAX-WS客户端
YourWebServiceClient client = new YourWebServiceClient();
YourWebServicePortType port = client.getYourWebServicePort();

// 获取BindingProvider配置上下文
BindingProvider bindingProvider = (BindingProvider) port;
Map<String, Object> requestContext = bindingProvider.getRequestContext();

// 创建带SNI支持的SocketFactory,替换成你的目标站点主机名
SSLSocketFactory defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SNIEnabledSSLSocketFactory sniFactory = new SNIEnabledSSLSocketFactory(defaultFactory, "your-target-host.com");

// 设置到JAX-WS请求上下文
requestContext.put(javax.net.ssl.SSLSocketFactory.class.getName(), sniFactory);
// 部分JAX-WS实现可能需要用这个键:com.sun.xml.ws.transport.https.client.SSLSocketFactory
requestContext.put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", sniFactory);

方法3:配置WildFly的出站SSL上下文

如果你的应用使用了WildFly的管理SSL配置,可以在standalone.xml或domain.xml中配置出站SSL上下文时指定SNI:

<subsystem xmlns="urn:jboss:domain:undertow:3.1">
    <!-- 其他配置 -->
    <server name="default-server">
        <!-- 其他配置 -->
        <https-listener name="https" socket-binding="https" ssl-context="your-ssl-context"/>
    </server>
    <ssl-contexts>
        <ssl-context name="your-ssl-context" trust-store="your-trust-store">
            <!-- 添加SNI配置 -->
            <server-name-indication value="your-target-host.com"/>
        </ssl-context>
    </ssl-contexts>
</subsystem>

然后确保JAX-WS客户端使用这个SSL上下文进行出站连接。

补充说明

为什么JUnit里正常?因为本地运行JUnit时用的是你的系统JVM,Java 8默认是启用SNI扩展的;而WildFly 10.1可能在启动时修改了JSSE的默认配置,或者其内嵌的连接池没有自动传递SNI信息,导致服务器无法识别请求的虚拟主机,抛出unrecognized_name错误。

内容的提问来源于stack exchange,提问作者user2412555

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:46:27