CXF +ws-security 和HttpURLConnection实现webservic请求

今天项目上面需要做一个CXF+ws-security和HttpURLConnection调用第三方的webService进行访问,这里我主要做客户端访问,服务器端和客户端完整请看原创。

客户端需要jar(纯java调用):

asm-3.3.jar 
commons-logging.jar 
cxf-2.7.18.jar 
cxf-api-2.7.18.jar 
cxf-rt-frontend-jaxws-2.7.8.jar

cxf-rt-ws-security-2.7.8.jar 

javapns-jdk16-2.2.1.jar 
neethi-3.0.3.jar 
stax2-api-3.1.4.jar 
woodstox-core-asl-4.4.1.jar 
wsdl4j-1.6.3.jar 
wss4j-1.6.19.jar 
xmlschema-core-2.1.0.jar 
xmlsec-1.5.8.jar
--------------------- 

 

maven依赖:

<!-- webService客户端start -->
       <dependency>
   			 <groupId>asm</groupId>
    		 <artifactId>asm</artifactId>
    		 <version>3.3</version>
		</dependency>
		<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf-api</artifactId>
    		<version>2.7.18</version>
		</dependency>
		<dependency>
    		<groupId>org.apache.cxf</groupId>
    		<artifactId>cxf</artifactId>
    		<version>2.7.18</version>
    		<type>pom</type>
		</dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>2.7.8</version>
        </dependency>
       
		<dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-ws-security</artifactId>
            <version>2.7.8</version>
        </dependency>
        
		<dependency>
			<groupId>com.github.fernandospr</groupId>
			<artifactId>javapns-jdk16</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.neethi</groupId>
			<artifactId>neethi</artifactId>
			<version>3.0.3</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.woodstox</groupId>
			<artifactId>stax2-api</artifactId>
			<version>3.1.4</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.woodstox</groupId>
			<artifactId>woodstox-core-asl</artifactId>
			<version>4.4.1</version>
		</dependency>
		<dependency>
			<groupId>wsdl4j</groupId>
			<artifactId>wsdl4j</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
    	          <groupId>org.apache.ws.security</groupId>
    		<artifactId>wss4j</artifactId>
    		<version>1.6.19</version>
		</dependency>
		<dependency>
			<groupId>org.apache.ws.xmlschema</groupId>
			<artifactId>xmlschema-core</artifactId>
			<version>2.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.santuario</groupId>
			<artifactId>xmlsec</artifactId>
			<version>1.5.8</version>
		</dependency>
                <dependency>
   		 	<groupId>jaxen</groupId>
    		<artifactId>jaxen</artifactId>
    		<version>1.1-beta-6</version>
		</dependency>
	<!-- webService客户端end -->

纯java调用webService包含两步:

一、创建验证辅助类,如本实列的PasswordHandler,用于向客户端传递密码。

二、创建调用类,如本实列的WsClientUtil,进行调用。

说明:
        同服务端一样,PasswordHandler也是实现了CallbackHandler接口,与服务器端不同的是客户端该类的用途只是为了设置密码,不许要做任何验证。详情见代码。

一、方法updateMobileInfo和getMobileInfo是使用cxf+ws-security做的webService安全请求验证。

二、方法getDirectoryEntryByAccount和resetAccountPassword是使用HttpURLConnection做的webService请求。

package com.hxzq.s0026.my168.util.webservice;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.dom4j.Attribute;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.hxzq.s0026.my168.util.LogUtil;
import com.thinkive.base.config.Configuration;
import com.thinkive.base.exception.CommonException;
import com.thinkive.base.util.StringHelper;

/**
 * 
 * @说明:调用第三方接口,采用CXF +ws-security 实现webservice客户端调用
 * @类型名称:WsClientUtil
 * @创建者: 敬进
 * @创建时间: 2018年10月30日 上午10:11:26
 * @修改者: 敬进
 * @修改时间: 2018年10月30日 上午10:11:26
 */
public class WsClientUtil {
 	private static JaxWsDynamicClientFactory dcf = null;
	private static WSS4JOutInterceptor wssOut = null;
	private static String url_90 = null;
	private static String url_91 = null;
	private static String namespace = null;
	static {
		// 获取配置文件信息
		url_90 = Configuration.getString("ws0002.url_90");
		url_91 = Configuration.getString("ws0001.url_91");
		namespace = Configuration.getString("ws0002.namespace");
		String account = Configuration.getString("ws0001.account");
		// 这个是用cxf 客户端访问cxf部署的webservice服务
		// 千万记住,访问cxf的webservice必须加上namespace ,否则通不过
		dcf = JaxWsDynamicClientFactory.newInstance();
		Map<String, Object> props = new HashMap<String, Object>();
		props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
		// 密码类型 明文:PasswordText密文:PasswordDigest
		props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
		// 用户名
		props.put(WSHandlerConstants.USER, account);
		// 将PasswordHandler 的类名传递给服务器,相当于传递了密码给服务器
		props.put(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.class.getName());
		props.put(WSHandlerConstants.MUST_UNDERSTAND, "0");
		wssOut = new WSS4JOutInterceptor(props);
	}

	/**
	 * 
	 * @说明: updateMobileInfo修改用户手机号方法
	 * @方法名称: updateMobileInfo
	 * @参数 @param loginName 用户名
	 * @参数 @param phone 电话号码
	 * @参数 @return
	 * @返回类型 String
	 * @创建者: 敬进
	 * @创建时间: 2018年10月30日 下午2:07:44
	 * @修改者: 敬进
	 * @修改时间: 2018年10月30日 下午2:07:44
	 */
	public static String updateMobileInfo(String loginName, String phone) {
		try {
			// 创建一个客户端
			LogUtil.info(url_91);
			Client client = dcf.createClient(url_91);
			// 添加访问参数
			LogUtil.info("------开始------");
			client.getOutInterceptors().add(wssOut);
			LogUtil.info("------添加访问参数------");
			// 执行方法
			Object[] objects = client.invoke("updateMobileInfo", loginName, phone);
			LogUtil.info("------执行方法------");
			return (String) objects[0];
		} catch (Exception e) {
			// TODO: handle exception
			LogUtil.error(e.getMessage());
 			throw new CommonException(1, "修改用户手机号失败!");
		}
	}

	/**
	 * 
	 * @说明:getMobileInfo获取用户联系信息方法
	 * @方法名称: getMobileInfo
	 * @参数 @param loginName 用户名
	 * @参数 @return
	 * @返回类型 String
	 * @创建者: 敬进
	 * @创建时间: 2018年10月30日 下午2:10:34
	 * @修改者: 敬进
	 * @修改时间: 2018年10月30日 下午2:10:34
	 */
	public static String getMobileInfo(String loginName) {
		try {
			Client client = dcf.createClient(url_91);
			client.getOutInterceptors().add(wssOut);
			Object[] objects = client.invoke("getMobileInfo", loginName);
			return (String) objects[0];
		} catch (Exception e) {
			// TODO: handle exception
			LogUtil.error(e.getMessage());
			throw new CommonException(1, "获取用户联系信息失败!");
		}
	}
	/**
	 * 、
	 * @说明: 获取密码过期时间方法
	 * @方法名称: getDirectoryEntryByAccount
	 * @参数 @param loginName 登录名
	 * @参数 @param password 密码
	 * @参数 @return 
	 * @返回类型 Map<String,Object>   key:Result、OperationMessage、BizData
	 * @创建者: 敬进 
	 * @创建时间: 2018年11月1日 上午10:29:09
	 * @修改者: 敬进 
	 * @修改时间: 2018年11月1日 上午10:29:09
	 */
	public static Map<String, Object> getDirectoryEntryByAccount(String loginName, String password) {
		StringBuilder sb = new StringBuilder("");
		sb.append(
				"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:oa=\"https://oa.hx168.com.cn/\">")
				.append("<soapenv:Header/>").append("<soapenv:Body>")
				.append("<oa:GetDirectoryEntryByAccount>")
				.append("<oa:adAccount>").append(loginName).append("</oa:adAccount>")
				.append("<oa:password>").append(password).append("</oa:password>")
				.append("</oa:GetDirectoryEntryByAccount>")
				.append("</soapenv:Body>").append("</soapenv:Envelope>");
		String dataXml = sb.toString();
		String soapAction = namespace + "GetDirectoryEntryByAccount";
		String resultXml = httpConnUtil(dataXml, soapAction);
		// xml解析
		Map<String, Object> resultMap=readStringXml(resultXml);
		return resultMap;
	}
	/**
	 * 
	 * @说明: 修改用户密码方法
	 * @方法名称: resetAccountPassword
	 * @参数 @param loginName 登录名
	 * @参数 @param oldPassword 旧密码
	 * @参数 @param newPassword 新密码
	 * @参数 @return
	 * @返回类型 Map<String,Object>   key:Result、OperationMessage、BizData
	 * @创建者: 敬进
	 * @创建时间: 2018年10月30日 下午2:14:38
	 * @修改者: 敬进
	 * @修改时间: 2018年10月30日 下午2:14:38
	 */
	public static Map<String, Object> resetAccountPassword(String loginName, String oldPassword, String newPassword) {
		StringBuilder sb = new StringBuilder("");
		sb.append(
				"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:oa=\"https://oa.hx168.com.cn/\">")
				.append("<soapenv:Header/>").append("<soapenv:Body>")
				.append("<oa:ResetAccountPassword>")
				.append("<oa:adAccount>").append(loginName).append("</oa:adAccount>")
				.append("<oa:oldPassword>").append(oldPassword).append("</oa:oldPassword>")
				.append("<oa:newPassword>").append(newPassword).append("</oa:newPassword>")
				.append("</oa:ResetAccountPassword>")
				.append("</soapenv:Body>").append("</soapenv:Envelope>");
		String dataXml = sb.toString();
		String soapAction = namespace + "ResetAccountPassword";
		String resultXml = httpConnUtil(dataXml, soapAction);
		// xml解析
		Map<String, Object> resultMap=readStringXml(resultXml);
		return resultMap;
	}
	/**
	 * 
	 * @说明: http请求处理webservice
	 * @方法名称: httpConnUtil
	 * @参数 @param dataXml xml入参
	 * @参数 @param soapAction 命名空间加方法名
	 * @参数 @return
	 * @返回类型 map
	 * @创建者: 敬进
	 * @创建时间: 2018年10月30日 下午2:12:24
	 * @修改者: 敬进
	 * @修改时间: 2018年10月30日 下午2:12:24
	 */
	private static String httpConnUtil(String dataXml, String soapAction) {
		String contentType = "text/xml; charset=utf-8";
		HttpURLConnection httpConn = null;
		OutputStream out = null;
		try {
			httpConn = (HttpURLConnection) new URL(url_90).openConnection();
			httpConn.setRequestProperty("Content-Type", contentType);
			if (null != soapAction) {
				httpConn.setRequestProperty("SOAPAction", soapAction);
			}
			httpConn.setRequestMethod("POST");
			httpConn.setDoOutput(true);
			httpConn.setDoInput(true);
			httpConn.connect();
			out = httpConn.getOutputStream();// 获取输出流对象
			httpConn.getOutputStream().write(dataXml.getBytes("UTF-8"));// 将要提交服务器的SOAP请求字符流写入输出流
			out.flush();
			out.close();
			int code = httpConn.getResponseCode();// 用来获取服务器相应状态
			String tempString = null;
			StringBuffer sb = new StringBuffer();
			if (code == HttpURLConnection.HTTP_OK) {
				BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
				while ((tempString = reader.readLine()) != null) {
					sb.append(tempString);
				}
				if (null != reader) {
					reader.close();
				}
			} else {
				BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getErrorStream(), "UTF-8"));
				// 一次读入一行,直到读入null为文件结束
				while ((tempString = reader.readLine()) != null) {
					sb.append(tempString);
				}
				if (null != reader) {
					reader.close();
				}
			}
			return sb.toString();
		} catch (Exception e) {
			// TODO: handle exception
			LogUtil.error(e.getMessage());
			throw new CommonException(1, "员工密码修改失败!");
		}
	}

	/**
	 * 
	 * @说明: dom4j解析xml
	 * @方法名称: readStringXml
	 * @参数 @param xml
	 * @参数 @return
	 * @返回类型 Map<String,Object>
	 * @创建者: 敬进
	 * @创建时间: 2018年11月1日 上午8:43:25
	 * @修改者: 敬进
	 * @修改时间: 2018年11月1日 上午8:43:25
	 */
	@SuppressWarnings("unchecked")
	private static Map<String, Object> readStringXml(String xml) {
		org.dom4j.Document doc = null;
		Map<String, Object> resultMap=null;
		try {
			doc =  DocumentHelper.parseText(xml);
			Element rootElt = doc.getRootElement();
			//获取根节点下body子节点
  			List<Element> elements = rootElt.selectNodes("soap:Body");
			if (elements.size()!=0) {
				for (Element element : elements) {
					Element parent=element.getParent();
					resultMap=new HashMap<String,Object>();
					arrayNodes(resultMap, element, rootElt);
					//如果存在多个item数据才调用这个
					List<Attribute> attributes = parent.attributes();
					if (attributes.size()>0) {
						for (Attribute attribute : attributes) {
							//将属性名(key)和属性值(value)添加到map对象中去
							resultMap.put(attribute.getName(), attribute.getValue());
						}
					}else{
						 LogUtil.info("该节点没有任何属性节点!");
 					}
				}
			}else{
				 LogUtil.info("xPath(DOM树路径)出现错误!");
 			} 
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
 			LogUtil.info(e.getMessage());
			throw new CommonException(1,"DOM解析失败!");
		}
		return resultMap;
	}
	 //一般类型的解析
	  private static void arrayNodes(Map<String, Object> nodeMap, Element node, Element root) {
	        LogUtil.info("----------------------------");
	        // 当前节点的名称、文本内容和属性
	        LogUtil.info("当前节点名称:" + node.getName());// 当前节点名称
	        if (!(node.getTextTrim().equals(""))) {
	        	LogUtil.info("当前节点的内容:" + node.getTextTrim());// 当前节点名称
	        }
	        String nodeName = node.getName();
	        String nodeValue = node.getTextTrim();
	        if (StringHelper.isNotEmpty(nodeValue)) {				
	        	nodeMap.put(nodeName, nodeValue);
			}
	        // 当前节点下面子节点迭代器
	        @SuppressWarnings("unchecked")
	        Iterator<Element> it = node.elementIterator();
	        // 遍历
	        while (it.hasNext()) {
	            // 获取某个子节点对象
	            Element e = it.next();
	            // 对子节点进行遍历
	            arrayNodes(nodeMap, e, root);
	        }
	    }
	
}

在做cxf+ws-security时需要传输一个password

package com.hxzq.s0026.my168.util.webservice;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

import com.thinkive.base.config.Configuration;
/**
 * 
 * @说明:webService验证辅助类,用于向ws-security发送密码
 * @类型名称:PasswordHandler
 * @创建者: 敬进 
 * @创建时间: 2018年10月30日 下午1:11:21
 * @修改者: 敬进 
 * @修改时间: 2018年10月30日 下午1:11:21
 */
public class PasswordHandler implements CallbackHandler {

	@Override
	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
		// TODO Auto-generated method stub
		String password=Configuration.getString("ws0001.password");
 		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc=(WSPasswordCallback) callbacks[i];
			pc.setPassword(password);
		}
	}

}

其中namespace是wsdl中的命名空间,url_90和url_91是webService接口。

使用HttpURLConnection做webService客户端时,需要拼接发送请求的参数,然后使用dom4j解析返回的参数,如

调用getDirectoryEntryByAccount方法,拼接参数类似。

 

 

使用方法readStringXml解析返回的参数。

cxf+ws-secur原创ity

webService调用方法还有axis和axis2

一般遇到的错误

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以在你的pom文件中添加以下依赖配置: ``` <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-core</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-bindings-soap</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-bindings-xml</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-databinding-jaxb</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-simple</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-addr</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-policy</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-wsdl</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> <version>1.6.3</version> </dependency> <dependency> <groupId>xml-resolver</groupId> <artifactId>xml-resolver</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.ws.xmlschema</groupId> <artifactId>xmlschema-core</artifactId> <version>2.2.5</version> </dependency> </dependencies> ``` 请注意,这些依赖的版本可能会随着时间的推移而更新。建议在使用这些依赖时,查阅官方文档以获取最新版本信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值