Spring WS Endpoint extract SOAP info - java

After reading doc on the Spring web site, still confused about how to extract information from a SOAP request.
For example, the SOAP request sent to server is like:
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:user="http://www.mysite.com/user/schemas">
<soapenv:Header/>
<soapenv:Body>
<user:UserRequest>
<!--You may enter the following 4 items in any order-->
<user:Key>key</user:Key>
<user:UserName>username</user:UserName>
<user:RequesterName>reqname</user:RequesterName>
<user:RequesterPassword>repw</user:RequesterPassword>
</user:UserRequest>
</soapenv:Body>
</soapenv:Envelope>
On my server side I create an Endpoint like:
#Endpoint
public class UserEndpoint {
private static final String NAMESPACE_URI = "http://www.mysite.com/user/schemas";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "UserRequest")
public void handleGetUserRequest() {
//Extract here...
}
}
How should I write extraction code here?

I would suggest having a look at the Spring WS samples for code ideas, depending on what else you are using in your application. For example: the HolidayEndpoint source code.

#Endpoint("myEndpoint")
public class MyEndpoint {
/**
* Spring-WS Endpoint
* #param submitSomethingRequest
* #param header
* #return SubmitSomethingResponse
*/
#PayloadRoot(namespace="http://my.namespace.org/spec/1.0.1", localPart="submitSomethingRequest")
#ResponsePayload
public SubmitSomethingResponse submitSomethingRequest(#RequestPayload SubmitSomethingRequest submitSomethingRequest, **SoapHeader header**) {
LOG.info("Received SOAP HEADER: " + header);
if(header != null) {
Iterator<SoapHeaderElement> hdrs = header.examineAllHeaderElements();
while(hdrs.hasNext()) {
SoapHeaderElement hdrEle = hdrs.next();
System.out.prinltn(hdrEle.getName().getPrefix() + ":" + hdrEle.getName().getLocalPart());
... //Do something here to parse DOM and extract headers you care about
}
}
...

Related

Wss4jSecurityInterceptor - My Custom Callback is interpreted as a CleanupCallback Object

I'm working in a project, made with Java 8 and Spring Boot, in which I want to add the Wss4jSecurityInterceptor for login purposes.
So far, this is what I've done in the WebServiceConfig class
#Bean
public AuthorizationCallBackHandler authorizationCallBackHandler(){
AuthorizationCallBackHandler callbackHandler = new AuthorizationCallBackHandler();
return callbackHandler;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor(){
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
securityInterceptor.setValidationActions("UsernameToken");
securityInterceptor.setValidationCallbackHandler(authorizationCallBackHandler());
return securityInterceptor;
}
#Override
public void addInterceptors(List interceptors) {
interceptors.add(securityInterceptor());
//interceptors.add(endPointInterceptor());
}
So, with this, every request that arrives at my Web Service, will be intercepted by the Wss4jSecurityInterceptor and will be handled by my custom callback, defined like this
public class AuthorizationCallBackHandler implements CallbackHandler{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
VnWsCredentialRepository credentialsRepo;
#Autowired
AuthUtility authUtil;
#Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
if (callbacks[0] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
String username = pc.getIdentifier();
VnWsCredential credentials = credentialsRepo.findByUsername(username);
logger.info("Request of authentication for username" + username);
String p = pc.getPassword();
// set the password on the callback. This will be compared to the
// password which was sent from the client.
if (credentials == null) {
pc.setPassword(null);
}else {
// String encodedPsw = authUtil.obtaindMD5Value(credentials.getPassword());
// pc.setPassword(encodedPsw);
pc.setPassword(credentials.getPassword());
}
}
if (callbacks[0] instanceof UsernameTokenPrincipalCallback) {
UsernameTokenPrincipalCallback pc = (UsernameTokenPrincipalCallback) callbacks[0];
pc.getPrincipal();
}
}
}
Here's my problem: when the Callback gets called, it receives an array that holds just 1 callback with a "CleanupCallback" type and I'm not, of course, able to do anything with it.
What am I missing in the following settings?
This is the SOAP call that I'm making with SOAP UI
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:it="some.fancy.ws">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-3967AEB46D733EF6E2154990461080350">
<wsse:Username>Just a user</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">just a password</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">pUn8VjdpVaIamSAIwXEeXg==</wsse:Nonce>
<wsu:Created>2019-02-16T17:03:30.803Z</wsu:Created>
</wsse:UsernameToken></wsse:Security>
</soapenv:Header>
<soapenv:Body>
<it:getPOrderRequest>
<it:poNumber>2197111225-F03292</it:poNumber>
</it:getPOrderRequest>
</soapenv:Body>
</soapenv:Envelope>
For anyone interested, I solved this thing by first of all removing from the request the content inside the header.
After that, I setted up an Outgoing WSS with the credentials and, just like that, the Wss4j Security Handler converted the Callback to the instance I wanted (that is, WSPasswordCallback).
Lesson learned: if Wss4j detects a some kind of error in handling the SOAP Request, it will generate a CleanupCallback and not an instance of WSPasswordCallback

Read SOAP request header with Spring

I am trying to read the SOAP request header from a endpoint in spring this way:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
#ResponsePayload
public GetCountryResponse getCountry(#RequestPayload GetCountryRequest request, MessageContext context) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
return response;
}
As you can see I have the MessageContext as a parameter in the handle method of the endpoint and I do the following in order to try to read the SOAP header coming from te request:
SaajSoapMessage soapRequest = (SaajSoapMessage) messageContext.getRequest();
SoapHeader reqheader = soapRequest.getSoapHeader();
while (itr.hasNext()) {
SoapHeaderElement ele = itr.next();
}
Apparently I am getting access to the SOAP header, but at this point I´m not really sure how to read the value of any SOAP header element, I´ve tried different approaches with no success.
For example, if the following SOAP request is coming from the soapUI I want to read the value 123456 from networkCode element:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<ns1:RequestHeader
soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next"
soapenv:mustUnderstand="0"
xmlns:ns1="https://www.google.com/apis/ads/publisher/v201508">
<ns1:networkCode>123456</ns1:networkCode>
<ns1:applicationName>DfpApi-Java-2.1.0-dfp_test</ns1:applicationName>
</ns1:RequestHeader>
</soapenv:Header>
<soapenv:Body>
<getAdUnitsByStatement xmlns="https://www.google.com/apis/ads/publisher/v201508">
<filterStatement>
<query>WHERE parentId IS NULL LIMIT 500</query>
</filterStatement>
</getAdUnitsByStatement>
</soapenv:Body>
</soapenv:Envelope>
Thanks in advance and best reards.
Yoy can you QName to extract data by using necessary tag
SaajSoapMessage soapRequest = (SaajSoapMessage) messageContext
.getRequest();
SoapHeader reqheader = soapRequest.getSoapHeader();
Iterator<SoapHeaderElement> itr = reqheader.examineAllHeaderElements();
while (itr.hasNext()) {
SoapHeaderElement testedElement = itr.next();
if (testedElement.getName()
.equals(new QName("https://www.google.com/apis/ads/publisher/v201508", "networkCode", "ns1"))) {
this.messageId = testedElement.getText();
break;
}
}
In SoapUI using Script assertion,we can do this:
As your request itself contains the Header Details, we can read any element of your Request xml using xpath.
Replace the TeststepName with your TestStepName.
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
def holder = groovyUtils.getXmlHolder( "TeststepName#Request" )
holder.namespaces["ns1"] = "https://www.google.com/apis/ads/publisher/v201508"
def y = holder["(//ns1:networkCode)"]
log.info "Value of networkCode"+ y
or
assert holder["(//ns1:networkCode)"]=='123456'
please try to get all the Header element from the Your Request like:
SaajSoapMessage soapRequest = (SaajSoapMessage) messageContext.getRequest();
Iterator HeaderList = soapRequest.getEnvelope().getHeader().examineAllHeaderElements();
while (HeaderList.hasNext()) {
SoapHeaderElement HeaderElements = HeaderList.next();
println("\n"+HeaderElements.getName().getLocalPart()+ " - "+HeaderElements.getText());
}
}

Spring WS: Add custom SOAP header

What's my goal?
I'm rather new to Spring WS, I got a WSDL (and along some XSDs, ofcourse) and i want to add some custom header elements to the SOAP response.
I've been searching the web, tried various code pieces, but it's all without any luck... nothing seems to work properly .
What's the problem?
The response SOAP message has a body what spring calls a Payload and my SOAP client (SOAPUI) receives the response rather well.
But here it comes: how should I add new (custom) SOAP headers to the response message?
What's the response xml expected?
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<aud:HeaderInfo xmlns:bd="http://www.myws.com/">
<bd:ID>123</bd:ID>
<bd:Type>text</bd:Type>
</aud:HeaderInfo>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ne:myWS xmlns:ne="http://www.iways.com/">
<ne:INFO>
<ne:NAME>JOHN</ne:NAME>
<ne:DESIGNATION>ITA</ne:DESIGNATION>
<ne:MOBILE>9841011113</ne:MOBILE>
</ne:INFO>
</ne:myWS>
My payload
#PayloadRoot(localPart = "myWSRequest", namespace = TARGET_NAMESPACE)
public #ResponsePayload myWSResponse getInfo(#RequestPayload myWSRequest request)
{
myWSResponse response = new myWSResponse();
Person person = personService_i.getAccountDetails(request.getID());
response.setPersonDetails(person);
return response;
}
Any side info?
i use xsd which generates a load of classes based upon the XSDs I don't know how to add those custom headers to the response message,
You could implement a endpointInterceptorAdapter and do the following:
public final class MyEndpointInterceptorAdapter extends EndpointInterceptorAdapter {
#Override
public boolean handleResponse(MessageContext messageContext_, Object endpoint_)
throws IOException {
WebServiceMessage _webServiceMessage = messageContext_.getResponse();
SoapMessage _soapMessage = (SoapMessage) _webServiceMessage;
if (_soapMessage != null) {
SoapEnvelope _soapEnvelope = _soapMessage.getEnvelope();
// create your qname object
QName _myQName = ....
// adding your quname to the header
_soapEnvelope.getHeader().addHeaderElement(myQName );
}
}
}
and in your spring configuration file, just add the interceptor:
<sws:interceptors>
<ref bean="myEndpointInterceptorAdapter"/>
</sws:interceptors>

JAX-WS Handler Ignoring Fault Sub-elements

I'm implementing a web service client using JAX-WS over SOAP. Its error codes are returned in the following way:
<?xml version = '1.0' encoding = 'UTF-8'?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<env:Header>
<!-- header stuff goes here -->
</env:Header>
<env:Body>
<env:Fault>
<abc:fault xmlns:abc="http://example.com/abc">
<abc:faultcode>12345</abc:faultcode>
<abc:faultstring>Error message goes here</abc:faultstring>
</abc:fault>
</env:Fault>
</env:Body>
</env:Envelope>
As far as I know, this is not the correct way to do SOAP faults. The subelements of a env:Fault should be <faultcode> and <faultstring>, not a different namespaced <fault>. Unfortunately, I have no way of making the web service change this.
My hope was that I would be able to parse this message in a SOAPHandler and transform it into a regular fault before passing it on to the rest of my code, however when I logged the message in an earlier Handler I saw that the Fault element completely empty. The <abc:fault> was gone!
I'm using JAX-WS on WebSphere 7 and I've tried setting "jaxws.payload.highFidelity" to true in my system properties. Any clues on to how to get at the original message?
Leaving this alone will cause a WebServiceException with a NullPointerException because JAX-WS can't find the faultcode.
So I found the answer to my question. WebSphere 7 uses Axis2. Axis2's MessageContext provides a property called "TRANSPORT_IN" which contains a ByteArrayInputStream. TRANSPORT_IN, as the name implies, contains the exact SOAP message received.
I parsed through the original SOAP message in my Handler#handleFault method using a SAXHandler to retrieve the abc:fault message. I then wrote the abc:fault > faultcode and faultstring to the soapenv:Fault faultcode and faultstring. My application then handles the SOAPFaultException as if it was a normal one.
I'm still very open to any better answers since this feels like roundabout way to do this.
Handler Code:
public boolean handleFault(SOAPMessageContext context) {
SOAPMessage m = context.getMessage();
if(m != null) {
SOAPBody body = m.getSOAPBody();
SOAPFault fault = body.getFault();
setAbcFault(fault, context);
}
}
private void setAbcFault(SOAPFault fault, MessageContext context) {
ByteArrayInputStream bis = (ByteArrayInputStream)context.get("TRANSPORT_IN");
// do sax parsing on the input stream
fault.setFaultCode(abcFaultCodeQName);
fault.setFaultString(abcFaultString);
}
If you are using JAX-WS, you can use SOAP faults. or that, you need an Exception with #WebFault annotation. You can find a good example in Using SOAP Faults and Exceptions in Java JAX-WS Web Services - Eben Hewitt on Java.
See the answer for returning null or throw exception and How to throw a custom fault on a JAX-WS web service?
Example:
#WebService
public class CalculatorWS {
public String factorial(int n) throws FactorialException {
if (n < 0) {
throw new FactorialException("Negative number!", // faultstring
"The number n = " + n); // detail
}
return BigIntegerMath.factorial(n).toString();
}
}
With:
public class FactorialException extends Exception {
String detail;
public FactorialException(String message, String detail) {
super(message);
this.detail = detail;
}
public String getFaultInfo() {
return detail;
}
}
If the request is:
<soapenv:Envelope ... >
<soapenv:Header/>
<soapenv:Body>
<test:factorial>
<arg0>-1</arg0>
</test:factorial>
</soapenv:Body>
</soapenv:Envelope>
The response is:
<soapenv:Envelope ... >
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring>Negative number!</faultstring>
<detail>
<ns2:FactorialExceptionBean xmlns:ns2="http://...">
The number n = -1
</ns2:FactorialExceptionBean>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
(Tested in Websphere 7)

How do I access SOAP headers in a spring soap endpoint?

Here is my SOAP request:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:str="http://app.strategyblocks.com/ws/schema/strategyblocks">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="...">
<wsse:Username>admin</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">secret</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<str:updateKpiRequest>
<str:company_id>1</str:company_id>
<str:kpi>
<str:external_id>1134511</str:external_id>
<str:title>title</str:title>
<str:description>description</str:description>
</str:kpi>
</str:updateKpiRequest>
</soapenv:Body>
</soapenv:Envelope>
Here is my Endpoint class:
#Endpoint
public class UpdateKpiEndpoint {
// The namespace of both request and response as declared in the XSD file
public static final String NAMESPACE_URI = "http://app.strategyblocks.com/ws/schema/strategyblocks";
// The local name of the expected request.
public static final String REQUEST_LOCAL_NAME = "updateKpiRequest";
#PayloadRoot(localPart = REQUEST_LOCAL_NAME, namespace = NAMESPACE_URI)
#ResponsePayload
public UpdateKpiResponse processUpdateKpi(#RequestPayload UpdateKpiRequest updateKpiRequest) {
try {
} catch (Exception e) {
UpdateKpiResponse response = new UpdateKpiResponse();
response.setCode("FAILURE");
response.setDescription("Problem with update kpi request");
return response;
}
UpdateKpiResponse response = new UpdateKpiResponse();
response.setCode("SUCCESS");
response.setDescription("Kpi has been updated");
return response;
}
}
At the moment I am passing a UsernameToken for authentication in the soap request, that is all working well and I have no problems with it what so ever. What I want to be able to achieve is to retrieve that username from the header in the body of processUpdateKpi method in my endpoint class, so that I can use it to find existing data for that user, I have tried to find examples of it being done and so far I have been unsuccessful, is it possible to do it? I have thought about also passing the username in the SOAP body as well, but I want to avoid it.
someone in the spring forums had a clear explanation on how to read the header from the endpoint class:
http://forum.springsource.org/showthread.php?109560-Unable-to-read-SoapHeader-in-Endpoint-class

Categories

Resources