依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-ws</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
WSDL转java
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generateDirectory>${basedir}/src/main/java</generateDirectory>
<schemas>
<schema>
<fileset>
<directory>${basedir}/src/main/resources/wsdl</directory>
<includes>
<include>*.wsdl</include>
<include>*.wsd</include>
</includes>
</fileset>
</schema>
</schemas>
</configuration>
</plugin>
</plugins>
</build>
mvn org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.13.2:generate
HTTPS签名
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.ws.transport.WebServiceMessageSender;
import org.springframework.ws.transport.http.ClientHttpRequestMessageSender;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;
@Slf4j
@Configuration
public class SoapConfig {
@Value("${mastercard.keystore.client.path}")
private String clientKeystorePath;
@Value("${mastercard.keystore.client.password}")
private String clientPassword;
@Bean
public WebServiceMessageSender webServiceMessageSender() throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
byte[] keystoreBytes;
try (InputStream inputStream = resolver.getResource(clientKeystorePath).getInputStream()) {
keystoreBytes = IOUtils.toByteArray(inputStream);
} catch (Exception e) {
log.error("https秘钥文件client.jks读取异常, keystore: {}", clientKeystorePath);
throw e;
}
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new ByteArrayInputStream(keystoreBytes), clientPassword.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keystore, clientPassword.toCharArray())
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
requestFactory.setConnectTimeout(10_000);
requestFactory.setReadTimeout(10_000);
ClientHttpRequestMessageSender messageSender = new ClientHttpRequestMessageSender();
messageSender.setRequestFactory(requestFactory);
return messageSender;
}
}
报文签名
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.soap.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
@Slf4j
@Component
public class SoapSignService {
private final KeyStore keystore;
private final String password;
private final String keystoreAlias;
@Value("${mastercard.system.institutionName}")
private String institutionName;
@Autowired
public SoapSignService(@Value("${mastercard.keystore.sign.path}") String path,
@Value("${mastercard.keystore.sign.password}") String password,
@Value("${mastercard.keystore.sign.alias}") String keystoreAlias) throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
byte[] keystoreBytes;
try (InputStream inputStream = resolver.getResource(path).getInputStream()) {
keystoreBytes = IOUtils.toByteArray(inputStream);
} catch (Exception e) {
log.error("签名秘钥文件signing.jks读取异常, keystore: {}", path);
throw e;
}
this.keystore = KeyStore.getInstance("PKCS12");
this.keystore.load(new ByteArrayInputStream(keystoreBytes), password.toCharArray());
this.password = password;
this.keystoreAlias = keystoreAlias;
}
private PrivateKey getKeyFormCert() throws Exception {
return (PrivateKey) keystore.getKey(keystoreAlias, password.toCharArray());
}
private java.security.cert.Certificate getCertificate() throws Exception {
return keystore.getCertificate(keystoreAlias);
}
public void sign(WebServiceMessage message) {
sign("", message);
}
public void sign(String appId, WebServiceMessage message) {
this.sign(appId, ((SaajSoapMessage) message).getSaajMessage());
}
public void sign(String appId, SOAPMessage soapMessage) {
try {
SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
soapEnvelope.setPrefix("soapenv");
soapEnvelope.removeNamespaceDeclaration("SOAP-ENV");
soapEnvelope.addNamespaceDeclaration("com", "http://common.ws.mcrewards.mastercard.com/");
SOAPHeader header = soapMessage.getSOAPHeader();
header.setPrefix("soapenv");
SOAPElement identity = header.addChildElement("identity", "com");
identity.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "IDENTITY");
SOAPElement appID = identity.addChildElement("appID");
appID.addTextNode(appId);
SOAPElement institutionName = identity.addChildElement("institutionName");
institutionName.addTextNode(this.institutionName);
SOAPBody soapBody = soapMessage.getSOAPBody();
soapBody.setPrefix("soapenv");
soapBody.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "Body");
signSOAPMessage(soapMessage);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
private void signSOAPMessage(SOAPMessage soapMessage) throws Exception {
SOAPElement soapHeader = soapMessage.getSOAPHeader();
SOAPElement securityElement = soapHeader.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
securityElement.addNamespaceDeclaration("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
java.security.cert.Certificate cert = getCertificate();
SOAPElement binarySecurityToken = addBinarySecurityToken(securityElement, soapMessage, cert);
SOAPElement timestamp = addTimestamp(securityElement, soapMessage);
PrivateKey key = getKeyFormCert();
SOAPElement securityTokenReference = addSecurityToken(securityElement);
addSignature(securityElement, key, securityTokenReference, soapMessage.getSOAPBody(), timestamp, binarySecurityToken);
}
private SOAPElement addSecurityToken(SOAPElement signature) throws SOAPException {
SOAPElement securityTokenReference = signature.addChildElement("SecurityTokenReference", "wsse");
SOAPElement reference = securityTokenReference.addChildElement("Reference", "wsse");
reference.setAttribute("URI", "#X509Token");
return securityTokenReference;
}
private void addSignature(SOAPElement signatureElement, PrivateKey privateKey, SOAPElement securityTokenReference, SOAPBody soapBody, SOAPElement timestamp, SOAPElement binarySecurityToken) throws Exception {
String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
javax.xml.crypto.dsig.DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod("http://www.w3.org/2001/04/xmlenc#sha512", null);
ArrayList<Transform> transformList = new ArrayList<>();
Transform envTransform = xmlSignatureFactory.newTransform("http://www.w3.org/2001/10/xml-exc-c14n#", (TransformParameterSpec) null);
transformList.add(envTransform);
ArrayList<Reference> referenceList = new ArrayList<>();
Reference x509 = xmlSignatureFactory.newReference("#X509Token", digestMethod, transformList, null, null);
Reference refTS = xmlSignatureFactory.newReference("#TS", digestMethod, transformList, null, null);
Reference refBody = xmlSignatureFactory.newReference("#Body", digestMethod, transformList, null, null);
Reference header = xmlSignatureFactory.newReference("#IDENTITY", digestMethod, transformList, null, null);
referenceList.add(x509);
referenceList.add(refTS);
referenceList.add(refBody);
referenceList.add(header);
javax.xml.crypto.dsig.CanonicalizationMethod cm = xmlSignatureFactory.newCanonicalizationMethod("http://www.w3.org/2001/10/xml-exc-c14n#", (C14NMethodParameterSpec) null);
javax.xml.crypto.dsig.SignatureMethod sm = xmlSignatureFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", null);
SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(cm, sm, referenceList);
DOMSignContext signContext = new DOMSignContext(privateKey, signatureElement);
signContext.setDefaultNamespacePrefix("ds");
signContext.putNamespacePrefix("http://www.w3.org/2000/09/xmldsig#", "ds");
signContext.setIdAttributeNS(soapBody, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id");
signContext.setIdAttributeNS(timestamp, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id");
signContext.setIdAttributeNS(binarySecurityToken, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id");
KeyInfoFactory keyFactory = KeyInfoFactory.getInstance();
DOMStructure domKeyInfo = new DOMStructure(securityTokenReference);
javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo = keyFactory.newKeyInfo(java.util.Collections.singletonList(domKeyInfo));
javax.xml.crypto.dsig.XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
signContext.setBaseURI("");
signature.sign(signContext);
}
private SOAPElement addTimestamp(SOAPElement securityElement, SOAPMessage soapMessage) throws SOAPException {
SOAPElement timestamp = securityElement.addChildElement("Timestamp", "wsu");
SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
timestamp.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "TS");
String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssX";
DateTimeFormatter timeStampFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
timestamp.addChildElement("Created", "wsu").setValue(timeStampFormatter.format(zonedDateTime.toInstant().atZone(ZoneId.of("UTC"))));
timestamp.addChildElement("Expires", "wsu").setValue(timeStampFormatter.format(zonedDateTime.plusSeconds(60).toInstant().atZone(ZoneId.of("UTC"))));
return timestamp;
}
private SOAPElement addBinarySecurityToken(SOAPElement securityElement, SOAPMessage soapMessage, java.security.cert.Certificate cert) throws Exception {
byte[] certByte = cert.getEncoded();
SOAPElement binarySecurityToken = securityElement.addChildElement("BinarySecurityToken", "wsse");
binarySecurityToken.setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
binarySecurityToken.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
binarySecurityToken.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "X509Token");
binarySecurityToken.addTextNode(Base64.getEncoder().encodeToString(certByte));
return binarySecurityToken;
}
}
调用层
import com.mastercard.mcrewards.ws.common.EmptyElement;
import com.mastercard.mcrewards.ws.diagnostic.ApplicationStatus;
import com.mastercard.mcrewards.ws.diagnostic.CurrentVersion;
import com.mastercard.mcrewards.ws.diagnostic.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.transport.WebServiceMessageSender;
import javax.xml.bind.JAXBElement;
@Service
public class SoapDiagnosticService extends WebServiceGatewaySupport {
private static final ObjectFactory factory = new com.mastercard.mcrewards.ws.diagnostic.ObjectFactory();
private static final String packagePath = "com.mastercard.mcrewards.ws.diagnostic";
private SoapSignService soapSignService;
@Autowired
public SoapDiagnosticService(WebServiceMessageSender messageSender,
SoapSignService soapSignService,
@Value("${mastercard.service.diagnosticServiceLink}") String link) {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(packagePath);
this.setDefaultUri(link);
this.setMarshaller(marshaller);
this.setUnmarshaller(marshaller);
this.setMessageSender(messageSender);
this.soapSignService = soapSignService;
}
public String doEcho(String request) {
JAXBElement<String> source = factory.createDoEcho(request);
WebServiceMessageCallback wsCallback = message -> soapSignService.sign(message);
JAXBElement response = (JAXBElement) getWebServiceTemplate().marshalSendAndReceive(source, wsCallback);
return (String) response.getValue();
}
}