Skip to content

Instantly share code, notes, and snippets.

@evil0th
Last active November 19, 2018 04:06
Show Gist options
  • Save evil0th/c2476b0138426858b560d55249cda766 to your computer and use it in GitHub Desktop.
Save evil0th/c2476b0138426858b560d55249cda766 to your computer and use it in GitHub Desktop.
SOAP工具类
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