Last active
November 19, 2018 04:06
-
-
Save evil0th/c2476b0138426858b560d55249cda766 to your computer and use it in GitHub Desktop.
SOAP工具类
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.liaow.soap.util; | |
import com.alibaba.fastjson.JSON; | |
import com.alibaba.fastjson.JSONArray; | |
import com.alibaba.fastjson.JSONObject; | |
import com.copote.nstamp.expection.BaseException; | |
import com.eviware.soapui.impl.WsdlInterfaceFactory; | |
import com.eviware.soapui.impl.wsdl.*; | |
import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder; | |
import com.eviware.soapui.support.XmlHolder; | |
import com.eviware.soapui.support.types.StringToStringsMap; | |
import com.eviware.soapui.support.xml.XmlUtils; | |
import lombok.extern.slf4j.Slf4j; | |
import org.apache.commons.collections.MapUtils; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.ws.security.WSConstants; | |
import org.apache.ws.security.WSSecurityException; | |
import org.apache.ws.security.message.WSSecHeader; | |
import org.apache.ws.security.message.WSSecUsernameToken; | |
import org.springframework.stereotype.Component; | |
import org.w3c.dom.Document; | |
import org.xml.sax.InputSource; | |
import org.xml.sax.SAXException; | |
import javax.wsdl.BindingOperation; | |
import javax.wsdl.OperationType; | |
import javax.xml.parsers.DocumentBuilder; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import javax.xml.parsers.ParserConfigurationException; | |
import java.io.IOException; | |
import java.io.StringReader; | |
import java.io.StringWriter; | |
import java.util.Map; | |
/** | |
* SOAP工具类 | |
* | |
* @author liao | |
* Create on 2018/10/22 15:10 | |
*/ | |
@Slf4j | |
@Component | |
public class SoapClient { | |
// 创建请求的默认命名 | |
private static final String DEFAULT_REQUEST_NAME = "request"; | |
// 定义默认命名空间 | |
private static final String NS_DEF_PREFIX = "soapenv"; | |
// 定义请求命名空间 | |
private static final String NS_REQ_PREFIX = "reqns"; | |
// 定义响应命名空间 | |
private static final String NS_RESP_PREFIX = "respns"; | |
// 默认命名空间JSON的key | |
public static final String JSONKEY_DEFAULT_NS = "soapenv"; | |
// 目标命名空间JSON的key | |
public static final String JSONKEY_TARGET_NS = "target"; | |
// XPATH JSON的key | |
public static final String JSONKEY_XPATH = "xpath"; | |
// 字段名称JSON的key | |
public static final String JSONKEY_FIELD = "field"; | |
// 字段名称集合JSON的key | |
public static final String JSONKEY_FIELDS = "fields"; | |
// 请求头JSON对象JSON的key | |
public static final String JSONKEY_HEADER = "header"; | |
// 认证JSON对象JSON的key | |
public static final String JSONKEY_AUTH = "auth"; | |
// 操作方法JSON的key | |
public static final String JSONKEY_OPERATION = "operation"; | |
private Map<String, WsdlInterface[]> wsdls = new LRULinkedHashMap<>(256); | |
public SoapClient() { | |
} | |
/** | |
* 发送请求 | |
* | |
* @param params 参数数组{"name":"wordKey","value":"hello",...} | |
* @param wsdlUrl 请求地址 | |
* @param requestDeclare 请求对象声明</br> | |
* {</br> | |
* "operation":"TranslatorString",</br> | |
* "soapenv":"http://schemas.xmlsoap.org/wsdl/",//默认命名空间,一般与xml中xmlns:wsdl一致</br> | |
* "target":"http://WebXml.com.cn/",//目标命名空间,与xml中targetNamespace一致</br> | |
* "fields":[{"field":"wordKey","xpath":"wordKey"}...],</br> | |
* "header":{"timeout":"60",...},//请求头对象声明</br> | |
* "auth":{ | |
* "username":"admin",//key:WSConstants.USERNAME_LN</br> | |
* "password":"passwd",//key:WSConstants.PASSWORD_LN</br> | |
* "passwordType":"PasswordDigest"//key:WSConstants.PASSWORD_TYPE_ATTR,value:WSConstants.PW_DIGEST</br> | |
* }//认证头对象声明</br> | |
* } | |
* @param responseDeclare 返回对象声明</br> | |
* {</br> | |
* "operation":"operationName",</br> | |
* "soapenv":"http://schemas.xmlsoap.org/wsdl/",</br> | |
* "target":"http://WebXml.com.cn/",</br> | |
* "fields":[{"field":"TranslatorStringResponse","xpath":"TranslatorStringResult/element()"},...],</br> | |
* } | |
* @param buildOptional 创建选填参数(依赖wdsl配置) | |
* @param alwaysBuildHeaders 创建SOAP头 | |
* @return | |
* @throws Exception | |
*/ | |
public JSON sendRequest(String wsdlUrl, JSONObject params, JSONObject requestDeclare, JSONObject responseDeclare, boolean buildOptional, boolean alwaysBuildHeaders) throws Exception { | |
// 获取操作 | |
WsdlOperation operationInst = getOperation(wsdlUrl, requestDeclare.getString(JSONKEY_OPERATION)); | |
// 组装请求消息 | |
WsdlRequest request = createRequest(operationInst, params, requestDeclare, buildOptional, alwaysBuildHeaders); | |
JSONObject header = requestDeclare.getJSONObject(JSONKEY_HEADER); | |
JSONObject auth = requestDeclare.getJSONObject(JSONKEY_AUTH); | |
// 设置请求头 | |
if (MapUtils.isNotEmpty(header)) { | |
setRequestHeader(request, header); | |
} | |
// 设置认证 | |
if (MapUtils.isNotEmpty(auth)) { | |
setSecurityHeader(request, auth); | |
} | |
// Request :Step 6 --->sending request to server..here we get celesius to faraneit converted value | |
WsdlSubmit submit = request.submit(new WsdlSubmitContext(request), false); | |
//Response:step1 fetch values from response | |
String response = submit.getResponse().getContentAsXml(); | |
log.debug(response); | |
JSON result = parseResult(response, responseDeclare); | |
return result; | |
} | |
public JSON sendRequest(String wsdlUrl, JSONObject params, JSONObject requestDeclare, JSONObject responseDeclare) throws Exception { | |
return sendRequest(wsdlUrl, params, requestDeclare, responseDeclare, true, true); | |
} | |
/** | |
* 解析返回数据 | |
* | |
* @param response | |
* @param responseDeclare | |
* @return | |
* @throws Exception | |
*/ | |
private JSON parseResult(String response, JSONObject responseDeclare) throws Exception { | |
JSONObject result = new JSONObject(); | |
//Response:step2 compare actual and expected value | |
XmlHolder respHolder = new XmlHolder(response); | |
respHolder.declareNamespace(NS_DEF_PREFIX, responseDeclare.getString(JSONKEY_DEFAULT_NS)); | |
//targetNamespace | |
respHolder.declareNamespace(NS_RESP_PREFIX, responseDeclare.getString(JSONKEY_TARGET_NS)); | |
JSONArray fields = responseDeclare.getJSONArray(JSONKEY_FIELDS); | |
for (int i = 0; i < fields.size(); i++) { | |
JSONObject o = fields.getJSONObject(i); | |
String[] values = respHolder.getNodeValues(String.format("//%s:%s", NS_RESP_PREFIX, o.getString(JSONKEY_XPATH))); | |
if (values.length == 1) { | |
result.put(o.getString(JSONKEY_FIELD), values[0]); | |
} else { | |
result.put(o.getString(JSONKEY_FIELD), values); | |
} | |
} | |
return result; | |
} | |
/** | |
* 创建请求 | |
* TODO 考虑入参是list情况 | |
* | |
* @param operationInst 操作 | |
* @param params 传入参数 | |
* @param buildOptional 创建选填参数(依赖wdsl配置) | |
* @param alwaysBuildHeaders 创建SOAP头 | |
* @return | |
* @throws Exception | |
*/ | |
private WsdlRequest createRequest(WsdlOperation operationInst, JSONObject params, JSONObject requestDeclare, boolean buildOptional, boolean alwaysBuildHeaders) throws Exception { | |
// Request : step3-->Build a request | |
WsdlRequest request = operationInst.addNewRequest(DEFAULT_REQUEST_NAME); | |
// Request : step4:Build envolope | |
WsdlInterface wsdlInterface = operationInst.getInterface(); | |
SoapMessageBuilder builder = new SoapMessageBuilder(wsdlInterface); | |
BindingOperation bindingOperation = operationInst.findBindingOperation(wsdlInterface.getWsdlContext().getDefinition()); | |
OperationType type = operationInst.getOperationType(); | |
if (OperationType.ONE_WAY.equals(type) || OperationType.REQUEST_RESPONSE.equals(type)) { | |
request.setRequestContent(builder.buildSoapMessageFromInput(bindingOperation, buildOptional, alwaysBuildHeaders)); | |
} else { | |
request.setRequestContent(builder.buildSoapMessageFromOutput(bindingOperation, buildOptional, alwaysBuildHeaders)); | |
} | |
// Request : step5 a--->Fetch the xml | |
String xmlRequest = request.getRequestContent(); | |
log.debug("oldRequest" + xmlRequest); | |
// Request :step5 b----->edit the xml | |
XmlHolder reqHolder = new XmlHolder(xmlRequest); | |
//wsdl:definitions | |
reqHolder.declareNamespace(NS_DEF_PREFIX, requestDeclare.getString(JSONKEY_DEFAULT_NS)); | |
//targetNamespace | |
reqHolder.declareNamespace(NS_REQ_PREFIX, requestDeclare.getString(JSONKEY_TARGET_NS)); | |
JSONArray fields = requestDeclare.getJSONArray(JSONKEY_FIELDS); | |
for (int i = 0; i < fields.size(); i++) { | |
JSONObject o = fields.getJSONObject(i); | |
reqHolder.setNodeValue(String.format("//%s:%s", NS_REQ_PREFIX, o.getString(JSONKEY_XPATH)), params.get(o.getString(JSONKEY_FIELD))); | |
} | |
String newRequest = reqHolder.getXml(); | |
log.debug("newRequest" + newRequest); | |
request.setRequestContent(newRequest); | |
return request; | |
} | |
/** | |
* 设置一般请求头 | |
* | |
* @param request | |
* @param header | |
*/ | |
private void setRequestHeader(WsdlRequest request, JSONObject header) { | |
StringToStringsMap stringToStringsMap = new StringToStringsMap(); | |
for (String key : header.keySet()) { | |
stringToStringsMap.put(key, header.getString(key)); | |
} | |
request.setRequestHeaders(stringToStringsMap); | |
} | |
/** | |
* 设置认证信息 | |
* | |
* @param request 请求 | |
* @param auth 认证信息</br> | |
* {</br> | |
* "username": "admin",//key:WSConstants.USERNAME_LN</br> | |
* "password": "passwd",//key:WSConstants.PASSWORD_LN</br> | |
* "passwordType": "PasswordDigest"//key:WSConstants.PASSWORD_TYPE_ATTR,value:WSConstants.PW_DIGEST</br> | |
* }</br> | |
*/ | |
private void setSecurityHeader(WsdlRequest request, JSONObject auth) { | |
String username = auth.getString(WSConstants.USERNAME_LN); | |
String password = auth.getString(WSConstants.PASSWORD_LN); | |
String passwordType = auth.getString(WSConstants.PASSWORD_TYPE_ATTR); | |
String req = request.getRequestContent(); | |
if (StringUtils.isAnyBlank(username, password, passwordType, req)) { | |
log.error("Request is missing username and password with password type when set security header"); | |
throw new BaseException("Request is missing username and password with password type when set security header"); | |
} | |
try { | |
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
dbf.setNamespaceAware(true); | |
DocumentBuilder db = dbf.newDocumentBuilder(); | |
Document doc = db.parse(new InputSource(new StringReader(req))); | |
WSSecUsernameToken addUsernameToken = new WSSecUsernameToken(); | |
if (WsdlRequest.PW_TYPE_DIGEST.equals(passwordType)) { | |
addUsernameToken.setPasswordType(WSConstants.PASSWORD_DIGEST); | |
} else { | |
addUsernameToken.setPasswordType(WSConstants.PASSWORD_TEXT); | |
} | |
addUsernameToken.setUserInfo(username, password); | |
// addUsernameToken.addNonce(); | |
// addUsernameToken.addCreated(); | |
StringWriter writer = new StringWriter(); | |
WSSecHeader secHeader = new WSSecHeader(); | |
secHeader.insertSecurityHeader(doc); | |
XmlUtils.serializePretty(addUsernameToken.build(doc, secHeader), writer); | |
request.setRequestContent(writer.toString()); | |
} catch (ParserConfigurationException pce) { | |
log.error("ParserConfigurationException occurred when set security header"); | |
throw new BaseException("ParserConfigurationException occurred when set security header" + pce.getMessage()); | |
} catch (IOException ie) { | |
log.error("IOException occurred when set security header"); | |
throw new BaseException("IOException occurred when set security header" + ie.getMessage()); | |
} catch (WSSecurityException wsse) { | |
log.error("WSSecurityException occurred when set security header"); | |
throw new BaseException("WSSecurityException occurred when set security header" + wsse.getMessage()); | |
} catch (SAXException saxe) { | |
log.error("SAXException occurred when set security header"); | |
throw new BaseException("SAXException occurred when set security header" + saxe.getMessage()); | |
} | |
} | |
/** | |
* 根据wsdl得到相应的operation | |
* | |
* @param wsdl | |
* @param operation | |
* @return | |
* @throws IOException | |
* @throws UnsupportedOperationException | |
*/ | |
private WsdlOperation getOperation(String wsdl, String operation) throws Exception, UnsupportedOperationException { | |
// 加载wsdl文件,找出operation | |
WsdlInterface[] wsdlInterfaces = getWsdlInterfaces(wsdl); | |
for (WsdlInterface wsdlInterface : wsdlInterfaces) { | |
//Request : Step2 as per notes--->pick an operation | |
WsdlOperation operationInst = wsdlInterface.getOperationByName(operation); | |
if (operationInst != null) { | |
return operationInst; | |
} | |
} | |
// 重新加载wsdl文件 | |
wsdls.remove(wsdl); | |
wsdlInterfaces = getWsdlInterfaces(wsdl); | |
for (WsdlInterface wsdlInterface : wsdlInterfaces) { | |
WsdlOperation operationInst = wsdlInterface.getOperationByName(operation); | |
if (operationInst != null) { | |
return operationInst; | |
} | |
} | |
throw new UnsupportedOperationException("Operation '" + operation + "' not supported by WSDL '" + wsdl + "'."); | |
} | |
/** | |
* 重新import wsdl文件,然后放入缓冲中 | |
* | |
* @param wsdl | |
* @return | |
* @throws IOException | |
*/ | |
private WsdlInterface[] getWsdlInterfaces(String wsdl) throws Exception { | |
try { | |
WsdlInterface[] wsdlInterfaces = wsdls.get(wsdl); | |
if (wsdlInterfaces == null) { | |
// Request : scan WSDL Step 1 in notes---->scan the wsdl | |
WsdlProject wsdlProject = new WsdlProject(); | |
wsdlInterfaces = WsdlInterfaceFactory.importWsdl(wsdlProject, wsdl, true); | |
wsdls.put(wsdl, wsdlInterfaces); | |
} | |
return wsdlInterfaces; | |
} catch (Exception e) { | |
log.error(e.getLocalizedMessage()); | |
throw new BaseException("Exception occurred import WSDL '" + wsdl + "'." + e.getMessage()); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment