使用Apache CXF构建Java客户端调用UKHO WebService遇问题求助
看起来你在使用Apache CXF调用UKHO的B2B WebService时遇到了问题,我帮你梳理下常见的坑和完整的解决方案——毕竟这个服务的配置确实有几个容易踩的点:
1. 先确认服务地址的正确性
你提供的两个地址:
https://extenavigator.ukho.gov.uk/serviceB2B/submitUKHOOrdering.svchttps://extenavigator.ukho.gov.uk/ServiceB2B/SubmitUKHOOrdering.svc?singleWsdl
注意路径的大小写:serviceB2B vs ServiceB2B,WebService地址通常是严格区分大小写的。建议你先通过浏览器访问带?singleWsdl的地址,确认WSDL能正常加载,然后确保代理工厂的setAddress参数和WSDL中声明的服务地址完全一致。
2. 处理HTTPS证书信任问题
目标服务是HTTPS协议,默认情况下Java客户端会验证服务器证书。UKHO的证书虽然是公共CA签发的,但如果你的环境信任库中没有包含对应CA,或者测试环境需要快速跳过验证,可以按以下两种方式处理:
测试环境:临时跳过证书验证(不推荐生产使用)
给CXF的HTTP客户端添加自定义信任配置,忽略证书和主机名检查:
import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.frontend.ClientProxy; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; // 创建代理后获取HTTP管道 IOrderingService client = (IOrderingService) proxyFactory.create(); HTTPConduit conduit = (HTTPConduit) ClientProxy.getClient(client).getConduit(); TLSClientParameters tlsParams = new TLSClientParameters(); // 自定义信任管理器:信任所有证书 TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); tlsParams.setDisableCNCheck(true); // 禁用主机名匹配检查 } catch (Exception e) { e.printStackTrace(); return; } conduit.setTlsClientParameters(tlsParams);
生产环境:导入证书到信任库
从浏览器访问服务地址,导出服务器证书,然后导入到Java默认信任库:
keytool -importcert -file ukho_service_cert.cer -keystore $JAVA_HOME/jre/lib/security/cacerts -alias ukho-b2b-service
默认信任库密码为changeit,导入后客户端会自动信任该证书。
3. 添加B2B服务的认证信息
UKHO的SubmitUKHOOrdering服务几乎肯定需要身份认证,最常见的是Basic认证。如果是这种情况,给HTTP管道添加认证配置:
import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier; // 继续上面的HTTPConduit配置 conduit.getAuthorization().setUserName("你的UKHO用户名"); conduit.getAuthorization().setPassword("你的UKHO密码"); conduit.getAuthorization().setAuthorizationType("Basic"); // 设置Basic认证供应商 conduit.setAuthSupplier(new DefaultBasicAuthSupplier());
如果服务要求WS-Security认证,你需要额外添加CXF的安全模块依赖,并配置对应的策略,不过UKHO的B2B服务大多用Basic认证,先优先尝试这个。
4. 完整的客户端示例代码
把上面的配置整合起来,完整的可运行代码大概是这样:
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier; import org.apache.cxf.frontend.ClientProxy; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; public class UKHOOrderClient { public static void main(String[] args) { JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean(); proxyFactory.setServiceClass(IOrderingService.class); // 注意地址大小写和WSDL一致 proxyFactory.setAddress("https://extenavigator.ukho.gov.uk/ServiceB2B/SubmitUKHOOrdering.svc"); // 创建客户端代理 IOrderingService client = (IOrderingService) proxyFactory.create(); // 配置HTTP管道 HTTPConduit conduit = (HTTPConduit) ClientProxy.getClient(client).getConduit(); // 处理HTTPS证书(生产环境请替换为信任库导入方式) TLSClientParameters tlsParams = new TLSClientParameters(); try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } 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()); tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); tlsParams.setDisableCNCheck(true); } catch (Exception e) { e.printStackTrace(); return; } conduit.setTlsClientParameters(tlsParams); // 配置Basic认证 conduit.getAuthorization().setUserName("你的UKHO用户名"); conduit.getAuthorization().setPassword("你的UKHO密码"); conduit.getAuthorization().setAuthorizationType("Basic"); conduit.setAuthSupplier(new DefaultBasicAuthSupplier()); // 调用服务方法 try { // 示例:构造请求并调用 OrderRequest request = new OrderRequest(); // 填充请求参数... OrderResponse response = client.submitOrder(request); System.out.println("服务响应:" + response); } catch (Exception e) { // 打印详细异常,包括SOAP Fault信息 e.printStackTrace(); } } }
5. 其他排查要点
- 确认
wsdl2java生成的代码正确:建议用带?singleWsdl的地址重新生成,命令示例:
确保生成的wsdl2java -p com.yourcompany.ukho https://extenavigator.ukho.gov.uk/ServiceB2B/SubmitUKHOOrdering.svc?singleWsdlIOrderingService接口和实体类没有编译错误。 - 检查CXF依赖完整性:如果用Maven,确保pom.xml包含必要依赖:
<dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.6.2</version> <!-- 使用最新稳定版 --> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.6.2</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-security</artifactId> <version>3.6.2</version> </dependency> </dependencies> - 检查网络连通性:确认你的机器能访问目标服务地址,没有防火墙或代理拦截。
如果还是有问题,一定要捕获并打印完整的异常栈(包括SOAP Fault的详细信息),这能帮你快速定位是认证错误、参数错误还是其他问题。
内容的提问来源于stack exchange,提问作者D.yan

