axis2 RPCServiceClient访问https接口访问踩过的坑!!!java版本,证书生成,TLS版本问题........

前几天,项目需要调用第三放的https的Webservice接口,说起来都是一把辛酸泪!!!

简单的说下实现以及踩过的坑!!!!!!!!!!!!!!!!

第一步:项目为maven项目,使用axis2需要导入相应jar包,下面是所需要的maven依赖:

        <dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2</artifactId>
			<version>1.6.2</version>
			<type>pom</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-adb</artifactId>
			<version>1.6.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-kernel</artifactId>
			<version>1.6.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-transport-http</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-transport-local</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-adb</artifactId>
			<version>1.6.2</version>
			<scope>compile</scope>
		</dependency>

第二步:编写接口调用逻辑:

第一种为匿名参数的格式,参数类似于<args0></args0>之类的,不需要设置参数名称:

好处:不用设置参数名称,返回值直接可以使用,下面是soapUI调用接口的返回值,第一种方式返回值为<return><return/>标签里面的值,我们可以直接使用。

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:ApplyKeyResponse xmlns:ns2="http://webservice.samples.pd.les.cn/">
         <return><![CDATA[<?xml version="1.0" encoding="utf-8"?><root><state>false</state><result>接口调用异常:IP地址错误</result></root>]]></return>
      </ns2:ApplyKeyResponse>
   </soap:Body>
</soap:Envelope>

/**
 * 调用Webservice公共接口 你们参数
 * @param corPlistByCorpName  接口调用方法名
 * @param xml 字符串类型的xml
 * @param webServiceUrl:接口地址不要添加?wsdl
 * @return
*/
@SuppressWarnings("rawtypes")
	public String useWebService(String corPlistByCorpName, String xml) {
		String result = "";
		try {
			Object[] requestParam = new Object[] { xml };
			RPCServiceClient serviceClient = new RPCServiceClient();
			// 指定调用WebService的URL
			EndpointReference targetEPR = new EndpointReference(webServiceUrl);
			Options options = serviceClient.getOptions();
			options.setTo(targetEPR);
			options.setTimeOutInMilliSeconds(30000);
			options.setProperty(HTTPConstants.CHUNKED, false);
            serviceClient.setOptions(options);
			// 指定方法返回值的数据类型的Class对象
			Class[] responseParam = new Class[] { String.class };
			// 指定要调用的getGreeting方法及WSDL文件的命名空间
			QName requestMethod = new QName(webServiceUrl, corPlistByCorpName);
			// 调用方法并输出该方法的返回值
			if (logger.isDebugEnabled()) {
				logger.debug("报送XML:" + xml);
			}
			result = (String) serviceClient.invokeBlocking(requestMethod, requestParam, responseParam)[0];
			if (logger.isInfoEnabled()) {
				logger.info("返回结果:" + result);
			}
		} catch (Exception e) {
			if (logger.isErrorEnabled()) {
				logger.error("报送异常:" + e.fillInStackTrace());
			}
			e.printStackTrace();
		}
		return result;
	}

第二种,需要对参数名称进行设置,我使用的是这个,因为我调用的接口需要用到参数名称

跟第一种的区别除了参数以外,我们还需要对返回值进行处理,OMElement results,返回值是 <soap:Body></soap:Body>里面的值,我们不可以直接使用。我用的是字符串截取获取我需要的节点里面的值,方法详见下面代码。

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:ApplyKeyResponse xmlns:ns2="http://webservice.samples.pd.les.cn/">
         <return><![CDATA[<?xml version="1.0" encoding="utf-8"?><root><state>false</state><result>接口调用异常:IP地址错误</result></root>]]></return>
      </ns2:ApplyKeyResponse>
   </soap:Body>
</soap:Envelope>

/**
 * 调用Webservice公共接口
 * @param corPlistByCorpName
 * @param requeststr
 * @param webServiceUrl:接口地址不要添加?wsdl
 * @return
*/
	@SuppressWarnings("rawtypes")
	public String useWebService(String corPlistByCorpName, String requeststr,String webServiceUrl) {
		String result = "";
		try {
			SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
			sslContext.init(null, null, null);
			SSLContext.setDefault(sslContext);
			RPCServiceClient serviceClient = new RPCServiceClient();
			// 指定调用WebService的URL
			EndpointReference targetEPR = new EndpointReference(webServiceUrl);
			Options options = serviceClient.getOptions();
			options.setTo(targetEPR);
			options.setTimeOutInMilliSeconds(30000);
			options.setProperty(HTTPConstants.CHUNKED, false);//不限制
options.setProperty(org.apache.axis2.Constants.Configuration.DISABLE_SOAP_ACTION, true);  //不设置的话会报错,忘记错误是什么了
			options.setTransportInProtocol(Constants.TRANSPORT_HTTPS);//没起到什么作用
			options.setAction(serviceNamespace+corPlistByCorpName);//命名空间+方法名
			serviceClient.setOptions(options);
			OMFactory fac = OMAbstractFactory.getOMFactory();
			// 这个和qname差不多,设置命名空间
			OMNamespace omNs = fac.createOMNamespace(serviceNamespace, corPlistByCorpName);
			OMElement data = fac.createOMElement(corPlistByCorpName, omNs);
			if(StringUtils.isBlank(requeststr)){
				// 对应参数的节点
				String[] strs = new String[] { "token" };
				// 参数值
				String[] val = new String[] { token };
				for (int i = 0; i < strs.length; i++) {
					OMElement inner = fac.createOMElement(strs[i], omNs);
					inner.setText(val[i]);
					data.addChild(inner);
				}
			}else{
				// 对应参数的节点
				String[] strs = new String[] { "token","requeststr" };
				// 参数值
				String[] val = new String[] { token,requeststr };
				for (int i = 0; i < strs.length; i++) {
					OMElement inner = fac.createOMElement(strs[i], omNs);
					inner.setText(val[i]);
					data.addChild(inner);
				}
			}
			// 发送数据,返回结果
			OMElement results = serviceClient.sendReceive(data);
			if(results!=null){
				result = results.toString();
				System.out.println("返回结果results==:" + results.toString());
			}else{
				System.out.println("results==null");
			}
		} catch (Exception e) {
			if (logger.isErrorEnabled()) {
				logger.error("报送异常:" + e.fillInStackTrace());
			}
			e.printStackTrace();
		}
		return result;
	}

/**
 * 获取指定标签中的内容 CDATA
 * @param xml   传入的xml字符串
 */
	public String getFieldListByCDATA(String xml) {
		String data = "";
		//正则表达式
		Pattern pattern = Pattern.compile("\\<\\!\\[CDATA\\[(?<text>[^\\]]*)\\]\\]\\>");
		Matcher m = pattern.matcher(xml);
		//匹配的有多个
		List<String> fieldList = new ArrayList<>();
		while (m.find()) {
			if (StringUtils.isNotEmpty(m.group(1).trim())) {
				data=m.group(1).trim();
			}
		}
		return data;
	}

	/**
	 * 获取指定标签中的内容
	 * @param xml   传入的xml字符串
	 * @param label 指定的标签
	 */
	public String getFieldListByRegex(String xml, String label) {
		String data = "";
		//正则表达式
		String regex = "<" + label + ">(.*?)</" + label + ">";
		Pattern pattern = Pattern.compile(regex);
		Matcher m = pattern.matcher(xml);
		//匹配的有多个
		List<String> fieldList = new ArrayList<>();
		while (m.find()) {
			if (StringUtils.isNotEmpty(m.group(1).trim())) {
				fieldList.add(m.group(1).trim());
			}
		}
		if(fieldList.size()>0){
			data = fieldList.get(0);
		}
		return data;
	}

第三步:接口调用过程中遇到的坑:

第一个问题:网上都说是更换TLS版本,我JKD用的是1.7的,默认版本是TLS,也就是1.0

org.apache.axis2.AxisFault: Remote host closed connection during handshake
    at org.apache.axis2.AxisFault.makeFault(AxisFault.java:430)
    at org.apache.axis2.transport.http.AxisRequestEntity.writeRequest(AxisRequestEntity.java:98)
    at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:499)
    at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)

    ...

Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:953)
    ... 96 more

Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(InputRecord.java:482)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:934)
    ... 103 more

针对这个问题,我百度了好久,试了很多种方式,跳过,或者是在全局设置,但是都没有起作用,

https://blog.csdn.net/xtj332/article/details/52228351/   跳过验证,我的是没有作用,可能是不会用吧

 

System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3"); 全局设置,我的也没有作用

试了很多方法都不行,我一度以为我的方向是错的,难道是证书问题?直到看到了一个贴子,设置soapui属性,可以访问https接口,我试着设置了下

在SoapUI-5.2.1.vmoptions 文件中加入:-Dsoapui.https.protocols=TLSv1.1,TLSv1.2

果然,接口可以调用了,这就证明我的方向没有错,既然跳不过,那就设置吧,疯狂百度后,

最后看到了一个帖子,试了下,然后就可以了:

解决方案:在接口发送请求前,调用以下代码:

SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
         sslContext.init(null, null, null);
         SSLContext.setDefault(sslContext);

ok,第一个问题解决,我中间怀疑是证书问题,事实证明是刚开始想的没错,先握手成功,才会去进行证书校验。

第二个问题:证书问题,也是试了好多绕过证书的方法,但是都没有成功,可能是太菜了吧,没办法,折腾证书吧!!!

报错信息如下:

sun.security.validator.ValidatorException: PKIX path building failed:

以前没弄过证书:就去百度:

第一种方式:用java代码生成:

java生成https安全证书,解决httpClient访问https出错

编译:javac InstallCert.java  (文件去资源里面去下载)

运行:java InstallCert 要访问的网址(域名/IP+端口)

结果:Enter certificate to add to trusted keystore or 'q' to quit: [1]

输入1确认生成jssecacerts文件,

将证书copy到$JAVA_HOME/jre/lib/security目录下

我的java是7的,默认的TLS是1.0版本的,服务端不是1.0版本的话,回报错(也就是TSL版本),所以我这种方式行不通,我就使用了第二种方式:

第二种方式:在浏览器下载证书,可以忽略证书问题(过期什么的),

格式务必选择正确,要不不能用!!!!

 接下来,使用 keytool 命令导入证书,进入到 JDK 下 jre 下 lib 下的 security 目录,比如我的是 C:\cs-softwares\Java\jdk1.8.0_111\jre\lib\security,然后运行命令 keytool -import -alias aliyun-maven -keystore cacerts -file C:\cs-softwares\aliyun-maven.cer ,如下,证书指纹(证书密钥):changeit
-alias :表示指定证书别名
-file :指定证书文件

1354412-20191026233528258-1487299684.pnguploading.4e448015.gif正在上传…重新上传取消1354412-20191026233528258-1487299684.pnguploading.4e448015.gif正在上传…重新上传取消

  

输入 Y 表示确认:

1354412-20191026233559627-190533902.pnguploading.4e448015.gif正在上传…重新上传取消

查看证书,证书密钥同样是 changeit :
keytool -list -keystore cacerts -alias aliyun-maven
  删除证书,证书密钥同样是 changeit:
keytool -delete -alias aliyun-maven -keystore cacerts

到此,再次运行,接口访问成功!!!!!!!搞定

这是带JDK里面设置,我们将其设置在项目中,然后在启动的时候调用下就可以了:

 

@PostConstruct
private void init() throws Exception {
   String trustStore = ResourceUtils.getFile(trustStorePath).getAbsolutePath();//获取证书在项目中存放的路径
   System.setProperty("javax.net.ssl.trustStore", trustStore);//设置地址
   System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);//设置密码 默认为changeit
}

ok,再次运行,接口调用成功!!!!!!!!!!!!!!!

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值