my main issue is this:
When the soap message is encrypted, the PayloadRootAnnotationMethodEndpointMapping is unable to map the soap message because The security interceptor did not have yet the time to decipher it. The solution is to replace #PayloadRoot with #SoapAction.
When a soap message is received, spring-ws calls first the PayloadRootAnnotationMethodEndpointMapping then SoapActionAnnotationMethodEndpointMapping
#Endpoint
public class assignedSubscriberNumberNotify {
private static final String NAMESPACE_URI = "http://test.aek.seavus.com/";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "ToOperatorChangeInASSNNotify")
#SoapAction("http://www.test.si/toOperatorChangeInASSNNotify")
#ResponsePayload
public void toOperatorChangeInASSNNotify(#RequestPayload ToOperatorChangeInASSNNotify request){
}
}
How can I decrypt request if client is unable to add SoapAction to the header?
tnx
miha
Related
I have a class annotated with #Endpoint and a handler method
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getWeatherRequest")
#SoapAction("http://foo/domain/getWeatherRequest")
#ResponsePayload
public GetWeatherResponse getWeatherRequest(#RequestPayload GetWeatherRequest request) {
// I want to get HTTP header (Not SOAP Header) here
}
Try using the #RequestHeader annotation
#SoapAction("http://foo/domain/getWeatherRequest")
#ResponsePayload
public GetWeatherResponse getWeatherRequest(#RequestPayload GetWeatherRequest request,
#RequestHeader("header-name") String header) {
}
You can use RequestHeader annotation in your method to access http headers, you can specify whether header is mandatory or optional using required attribute
#RequestHeader(value = "ConfigId", required = true) String configId
I have an authorization server [Simple Class annotated with #SpringBootApplication,
#RestController,#Configuration,#EnableAuthorizationServer & oauth2 security] running on port 8081 which works fine & provides the access token when requested from POSTMAN using POST method along with needful parameters in the form of key value pair,
http://localhost:8080/oauth/token, but how should i implement the camel route in java to get the access token by passing parameters in body ?
This question is more about sending multipart/form-data with Apache Camel. I was playing with it some time ago and solved it with custom Processor, converting headers to multipart/form-data format with Content-Disposition: form-data.
This is my Processor converting headers to multipart/form-data format:
public class PrepareMultipartFormData implements Processor {
private String[] multipartHeaders;
public PrepareMultipartFormData(String... multipartHeaders) {
this.multipartHeaders = multipartHeaders;
}
#Override
public void process(Exchange exchange) throws Exception {
addMultipart(exchange.getIn(), multipartHeaders);
}
private static void addMultipart(Message message, String... multipartKeys){
final String boundary = "---------------------------"+RandomStringUtils.randomAlphanumeric(9);
message.setHeader(Exchange.CONTENT_TYPE, "multipart/form-data;boundary="+boundary);
StringBuilder sb = new StringBuilder("--").append(boundary);
for (String key: multipartKeys) {
sb.append("\r\n")
.append("Content-Disposition: form-data; name=\"").append(key).append("\"")
.append("\r\n\r\n")
.append(message.getHeader(key, String.class))
.append("\r\n")
.append("--").append(boundary);
}
message.setBody(sb.toString());
}
}
To OAuth request token you need to send:
HTTP headers
Authorization header - This is part of standard HTTP component specified by endpoint options authUsername and authPassword
Content-Type - This is added in my PrepareMultipartFormData Processor
Form data - These are converted from headers in PrepareMultipartFormData Processor
grant_type
username
password
client_id
Final route can be implemented in this way:
(Replace constants with some expressions, to set it dynamically. If you need only token in response, add some unmarshalling, since this route returns JSON)
from("direct:getTokenResponse")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.HTTP_PATH, constant("oauth/token"))
.setHeader("grant_type", constant("password"))
.setHeader("username", constant("admin"))
.setHeader("password", constant("admin1234"))
.setHeader("client_id", constant("spring-security-oauth2-read-write-client"))
.process(new PrepareMultipartFormData("grant_type", "username", "password", "client_id"))
.to("http://localhost:8080?authMethod=Basic&authUsername=oauth-endpoint-username&authPassword=oauth-endpoint-password")
.convertBodyTo(String.class)
.to("log:response");
Updating answer to provide a bit shorter implementation of PrepareMultipartFormData#addMultipart using MultipartEntityBuilder.
private static void addMultipart(Message message, String... multipartKeys) throws Exception{
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
for (String key: multipartKeys) {
builder.addTextBody(key, message.getHeader(key, String.class));
}
HttpEntity resultEntity = builder.build();
message.setHeader(Exchange.CONTENT_TYPE, resultEntity.getContentType().getValue());
message.setBody(resultEntity.getContent());
}
When I send a SOAP request to the server it returns following error, though I send similar request using SoapUI and that works. It seems I need to change my SOAP request to the one that I am sending using SoapUI. WSDL is here.
[ truncated ] System.Web.Services.Protocols.SoapException : The value of the
HTTP header ' SOAPAction ' was not recognized by the server . \ r \ n at
System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest ( )
\ r \ n at System.Web.Servic
I am sending following request using Java
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:SearchFlights xmlns:ns2="ElysArres.API">
<ns2:SoapMessage>
<ns2:Username>Test</ns2:Username>
<ns2:Password>TestPassword</ns2:Password>
<ns2:LanguageCode>EN</ns2:LanguageCode>
<ns2:Request>
<ns2:Departure>ONT</ns2:Departure>
<ns2:Destination>EWR</ns2:Destination>
<ns2:DepartureDate>2016-01-20</ns2:DepartureDate>
<ns2:ReturnDate>2016-01-28</ns2:ReturnDate>
<ns2:NumADT>1</ns2:NumADT>
<ns2:NumINF>0</ns2:NumINF>
<ns2:NumCHD>0</ns2:NumCHD>
<ns2:CurrencyCode>EUR</ns2:CurrencyCode>
<ns2:WaitForResult>true</ns2:WaitForResult>
<ns2:NearbyDepartures>true</ns2:NearbyDepartures>
<ns2:NearbyDestinations>true</ns2:NearbyDestinations>
<ns2:RROnly>false</ns2:RROnly>
<ns2:MetaSearch>false</ns2:MetaSearch>
</ns2:Request>
</ns2:SoapMessage>
</ns2:SearchFlights>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I can send following request using SoapUI and it works
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:els="ElsyArres.API">
<soap:Header/>
<soap:Body>
<els:SearchFlights>
<els:SoapMessage>
<els:Username>Test</els:Username>
<els:Password>TestPassword</els:Password>
<els:LanguageCode>EN</els:LanguageCode>
<els:Request>
<els:Departure>ONT</els:Departure>
<els:Destination>EWR</els:Destination>
<els:DepartureDate>2016-01-20</els:DepartureDate>
<els:ReturnDate>2016-01-28</els:ReturnDate>
<els:NumADT>1</els:NumADT>
<els:NumINF>0</els:NumINF>
<els:NumCHD>0</els:NumCHD>
<els:CurrencyCode>EUR</els:CurrencyCode>
<els:WaitForResult>true</els:WaitForResult>
<els:NearbyDepartures>true</els:NearbyDepartures>
<els:NearbyDestinations>true</els:NearbyDestinations>
<els:RROnly>false</els:RROnly>
<els:MetaSearch>false</els:MetaSearch>
</els:Request>
</els:SoapMessage>
</els:SearchFlights>
</soap:Body>
</soap:Envelope>
I am not sure how to make the request that I am creating with Java same as what I am sending with SoapUI.
Code
SearchFlights
#XmlRootElement(name = "SearchFlights")
#XmlAccessorType(XmlAccessType.FIELD)
public class SearchFlights {
#XmlElement(name = "SoapMessage")
private SoapMessage soapMessage;
getter and setter
SoapMessage
#XmlRootElement(name = "SoapMessage")
#XmlAccessorType(XmlAccessType.FIELD)
public class SoapMessage {
#XmlElement(name = "Username")
private String username;
#XmlElement(name = "Password")
private String password;
#XmlElement(name = "LanguageCode")
private String languageCode;
#XmlElement(name = "Request")
private Request request;
getters and setters
Request
#XmlRootElement(name = "Request")
#XmlAccessorType(XmlAccessType.FIELD)
public class Request {
#XmlElement(name = "Departure")
private String departure;
#XmlElement(name = "Destination")
private String destination;
#XmlElement(name = "DepartureDate")
private String departureDate;
#XmlElement(name = "ReturnDate")
private String returnDate;
#XmlElement(name = "NumADT")
private int numADT;
#XmlElement(name = "NumINF")
private int numInf;
#XmlElement(name = "NumCHD")
private int numCHD;
#XmlElement(name = "CurrencyCode")
private String currencyCode;
#XmlElement(name = "WaitForResult")
private boolean waitForResult;
#XmlElement(name = "NearByDepartures")
private boolean nearByDepartures;
#XmlElement(name = "NearByDestinations")
private boolean nearByDestinations;
#XmlElement(name = "RROnly")
private boolean rronly;
#XmlElement(name = "MetaSearch")
private boolean metaSearch;
getters and setters
package-info.java
#XmlSchema(
namespace = "ElsyArres.API",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.myproject.flights.wegolo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
jaxb.index
SearchFlights
Flight
Flights
Leg
Legs
Outbound
Request
Response
SoapMessage
Code to send request
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPConstants;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
......
// populate searchFlights and other classes to create request
try {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.myproject.flights.wegolo");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights);
Response msg = (Response) response;
System.err.println("Wegolo >>>"
+ msg.getFlights().getFlight().size());
} catch (Exception s) {
s.printStackTrace();
}
Update
I removed package-info.java and managed to use the suggested code, but it is still sending the same header.
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
{
((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
}
}
);
SOAP Version 1.1 requires a HTTP header in your SOAP request to specify the SOAP action. It's not in the actual XML, it's part of the request (in the HTTP header), so that is why you are not seeing any difference between your SoapUI request xml, and the request you're sending using the WebServiceTemplate. Soap 1.2 allows you to set it as an attribute on the media type, but that is not valid for a 1.1 server. Note that according to the specification, the value you use doesn't have to be resolvable.
SOAP places no restrictions on the format or specificity of the URI or that it is resolvable. An HTTP client MUST use this header field when issuing a SOAP HTTP Request.
Usually, it's specified in your WSDL, something like (taken from here):
<soap:operation
soapAction="http://www5v80.elsyarres.net/searchFlights"
style="document" />
If that is not in your WSDL, you can add it by using the action annotation in spring in your webservice endpoint class.
#Endpoint
public class MyFlightEndpoint{
#Action("http://www5v80.elsyarres.net/searchFlights")
public SearchFlights request() {
...
}
}
If it is in your WSDL, you'll want to place that value into your HTTP header on the client side. To do this, you then need to get access to the message on the client side after it's created, but before it's sent in order to add the action header. Spring provides a message callback interface for that, that's described here. What you'll want to do is something like:
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"http://www5v80.elsyarres.net/service.asmx",
searchFlights,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
{
((SoapMessage)message).setSoapAction("http://www5v80.elsyarres.net/searchFlights");
}
}
);
There's a discussion on SOAP action headers here, and the point (or lack of a point) for them if you want to know more.
Edit: So looking at the wsdl here:
<soap:operation soapAction="ElsyArres.API/SearchFlights" style="document"/>
you'll want the following action:
ElsyArres.API/searchFlights
Now just update the code to read
((SoapMessage)message).setSoapAction("ElsyArres.API/searchFlights");
and you're good to go!
Edit 2: I also notice the service you're connecting to accepts SOAP 1.2 connections, while you're using SOAP 1.1. You can force your client to use SOAP 1.2 by setting it in your factory.
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
It looks like the server uses the same endpoint, so that should be the only change.
I had the same problem, my fix was :
#Configuration
public class SoapConfiguration {
private static final String SOAP_1_2_PROTOCOL= "SOAP 1.2 Protocol";
#Bean
public WebServiceTemplate webServiceTemplate() throws Exception {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("YOUR_WSDL_GENERATED_PATH");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
return webServiceTemplate;
}
And my SoapService
#Service
#RequiredArgsConstructor
public class SoapDomainBoxService extends WebServiceGatewaySupport {
private final WebServiceTemplate webServiceTemplate;
public void searchFlights(SearchFlights searchFlights) {
String url = "YOUR.URL.asmx";
Response response = (Response) webServiceTemplate.marshalSendAndReceive(url, searchFlights, new SoapActionCallback("ACTION.CALLBACK"));
}
Very important on creation of message factory use
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(javax.xml.soap.MessageFactory.newInstance(SOAP_1_2_PROTOCOL));
Another way to add SOAPAction header when using WebServiceGatewaySupport is to do the following:
getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback("http://httpheader/"));
This is using messageFactory.setSoapVersion(SoapVersion.SOAP_12);
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>
I need to authentication SOAP header and give the response accordingly in my web service. The authentication header will verify the userId and password information.
If authenticated, then SOAP body of the request will be processed, else Invalid Authentication message will be returned
Below is my controller
package com.ws.controller;
#Endpoint
public class MyWSEndpoint
#Autowired(required=true)
private WSService service;
#PayloadRoot(localPart = "myWSRequest", namespace = Constants.TARGET_NAMESPACE)
public #ResponsePayload MyWSResponse getInfo(#RequestPayload MyWSRequest request) throws Exception
{
MyWSResponse response = new MyWSResponse();
response=service.getResponse();
return response;
}
}
i'm using Spring + SOAP
Please advise if i do right or better approach to solve.
Any working samples or projects will be much appreciated