Https Webservice接口的免证书调用

前言

在调用https协议的Webservice接口时,如果没有做证书验证,一般会报javax.net.ssl.SSLHandshakeException错误,如果是Was中间件,更会产生jar依赖问题。

一般的解决方案是利用jdk的keytool(ikeyman)生成公钥并导入进jre的信任公钥库中。这种方法略显繁琐复杂。

本文以Axis、httpclient(其他客户端同理)调用方式为例,说明在不导入证书的情况下如何绕过验证调用Https协议的Webservice接口,适用于Sun/Oracle JDK和IBM JDK(was中间件)。

思路

client发起https调用时,会使用默认的SocketFactory,对服务端证书进行验证,由于没有正确的公钥验签,是无法握手成功的。因此,对于自签名的服务端,我们需要重写一个SocketFactory,跳过证书验证,并用这个SocketFactory替换client本身默认的SocketFactory。这种方案不仅适用于Webservice框架的客户端,亦常用于Httpclient发起的https调用。

方案

Axis调用

在调用Webservice接口时,替换axis.socketSecureFactory。

使用Sun/Oracle JDK的情况下可以直接用如下替换:

AxisProperties.setProperty("axis.socketSecureFactory","org.apache.axis.components.net.SunFakeTrustSocketFactory");

也可以写一个自定义的SocketFactory类进行替换(同时适用于IBM JDK):继承Axis的父类JSSESocketFactory ,并且重写父类initFactory方法

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Hashtable;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.axis.components.net.JSSESocketFactory;

/**
 * 跳过HTTPS验证
 * @since 2022-06-09
 */
public class MyTLSSocketSecureFactory extends JSSESocketFactory {

	public MyTLSSocketSecureFactory(Hashtable attributes) {
		super(attributes);
		// TODO Auto-generated constructor stub
	}

	// Create a trust manager that does not validate certificate chains

	X509TrustManager trustManager = new X509TrustManager() {
		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}

		@Override
		public void checkClientTrusted(X509Certificate[] xcs, String str) {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] xcs, String str) {
		}
	};
	TrustManager[] trustAllCerts = new TrustManager[] { trustManager };

	@Override
	public void initFactory() throws IOException {
		SSLContext ctx;
		try {
			ctx = SSLContext.getInstance("TLSv1.2");
//			ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
			ctx.init(null, trustAllCerts, null);
			// ctx.init(null, trustAllCerts, new java.security.SecureRandom());
			sslFactory = ctx.getSocketFactory();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

在调用前设置:

// 在客户端的ServiceSoapBindingStub.createCall()中设置
AxisProperties.setProperty("axis.socketSecureFactory", MyTLSSocketSecureFactory.class.getName());

Webservice接口调用:

try {
    URL endpoint = new URL("https://host:port/WebService?wsdl");
    webservice = new WebServiceLocator().getWebServicePort(endpoint);
} catch (Exception e) {
    e.printStackTrace();
}

HttpClient调用

可以使用Httpclient作为Webservice客户端发起免征书调用,优点是兼容性更强,缺点是需要自定义封装和解析soap xml报文。

Httpclient依赖jar:httpcore-4.4.4.jar,httpclient-4.5.jar(在was中间件下需要设置共享库,否则会发生jar冲突)

  1. 获取调用报文

可以通过soapUI调试Webservice接口,获取调用时发送的soap xml格式。

  1. 创建绕过SSL验证的Httpclient客户端
	 /**
     * 在调用SSL之前需要重写验证方法,取消检测SSL
     * 创建ConnectionManager,添加Connection配置信息
     * @return HttpClient 支持https
     */
	private static HttpClient getHttpClient() {
		try {
			// 在调用SSL之前需要重写验证方法,取消检测SSL
			X509TrustManager trustManager = new X509TrustManager() {
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}

				@Override
				public void checkClientTrusted(X509Certificate[] xcs, String str) {
				}

				@Override
				public void checkServerTrusted(X509Certificate[] xcs, String str) {
				}
			};
			SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
			ctx.init(null, new TrustManager[] { trustManager }, null);
			SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
			// 创建Registry
			RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).setExpectContinueEnabled(Boolean.TRUE)
					.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
					.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(socketTimeout).build();
			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
					.register("http", PlainConnectionSocketFactory.INSTANCE).register("https", socketFactory).build();
			// 创建ConnectionManager,添加Connection配置信息
			PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
			CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
					.setDefaultRequestConfig(requestConfig).build();
			
			return closeableHttpClient;
		} catch (KeyManagementException ex) {
			throw new RuntimeException(ex);
		} catch (NoSuchAlgorithmException ex) {
			throw new RuntimeException(ex);
		}
	}
  1. 发起Webservice接口调用
    public static String doPost(String url, String soapXml, String soapAction) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse httpResponse = null;
        String result = "";
        // 创建httpClient实例
        httpClient = (CloseableHttpClient) getHttpClient();
        
        // 创建httpPost远程连接实例
        HttpPost httpPost = new HttpPost(url);
        
        // 设置请求头
        httpPost.addHeader("Content-Type", "text/xml;charset=UTF-8");
        httpPost.setHeader("SOAPAction", SOAPAction);
        httpPost.setEntity(new StringEntity(soapXml, "UTF-8"));

        try {
            // httpClient对象执行post请求,并返回响应参数对象
            httpResponse = httpClient.execute(httpPost);
            // 从响应对象中获取响应内容
            HttpEntity entity = httpResponse.getEntity();
            result = EntityUtils.toString(entity);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			Logger.error("发送POST请求异常:" + e.getMessage(), e);
		} catch (IOException e) {
			e.printStackTrace();
			Logger.error("发送POST请求异常:" + e.getMessage(), e);
        } finally {
            // 关闭资源
            if (null != httpResponse) {
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
  1. 解析返回的result XML。此处不做详解。

参考链接

HttpClient调用WebService接口_小豆的编程世界...的博客

httpclient作为客户端调用webservice_aperise的博客

posted @ 2022-07-15 11:19  YaenLi  阅读(2488)  评论(0编辑  收藏  举报