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
相关产品推荐
相关产品推荐

