Getting Request parameters from Spring WS Interceptor - java

I am using Jaxb 2 with a Spring WS, and I have an interceptor which is directed to a particular payload and it works fine.
Here my requirement is to read the request parameters from the handleRequest method of my interceptor. I know this should be fairly straight forward. However could not figure out a way to read the request parameters. At the moment my handleRequest method looks as below.
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws Exception {
boolean proceed = true;
SaajSoapMessage saajSoapMessage =
(SaajSoapMessage) messageContext.getRequest();
SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
Document doc = saajSoapMessage.getDocument();
Element element = doc.getElementById("request");
}
The relevant part of the my Endpoint class is
#PayloadRoot(namespace = NAMESPACE, localPart = "confirOrderRequest")
public #ResponsePayload ConfirmOrderResponse handleConfirmOrder(
#RequestPayload ConfirmOrderRequest confirmOrderRequest) {
...........
}
Here my requirement is to get the orderId which comes with the ConfirmOrderRequest in the interceptor handleRequest method, is there a way to do this directly, or do I need to do some XML parsing for that?

#VitualTroll, It helped me somewhat, thanks !
But answer on that question is incorrect(at least in my case). Body of my new handleRequest() method would looks as follows. Hope this would save some time for someone else in future. Here jaxb2Marshaller is my spring bean.
#Autowired
private Jaxb2Marshaller jaxb2Marshaller;
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
boolean proceed = true;
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) messageContext.getRequest();
SoapBody requestBody = saajSoapMessage.getSoapBody();
Object obj = jaxb2Marshaller.unmarshal(requestBody.getPayloadSource());
if (obj instanceof ConfirmOrderRequest ) {
ConfirmOrderRequest cor = (ConfirmOrderRequest ) obj;
String orderId = cor.getOrderId();
...........
.......
}
.....
}

Related

Get request header in spring boot

How do I get the header and body of the current request from an application which called my Springboot application? I need to extract this information. Unfortunately this does not work. I tried to get the current request with this code sample (https://stackoverflow.com/a/26323545/5762515):
public static HttpServletRequest getCurrentHttpRequest(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
return request;
}
throw new IllegalArgumentException("Request must not be null!");
}
And then I tried to get the body
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) currentRequest;
String requestBody = new String(requestWrapper.getContentAsByteArray());
Can someone tell me what im doing wrong?
Thanks in advance
#RestController
public class SampleController {
#PostMapping("/RestEndpoint")
public ResponseEntity<?> sampleEndpoint(#RequestHeader Map<String, String> headers,#RequestBody Map<String,String> body) {
//Do something with header / body
return null;
}
}
If the application's are communicating through a rest endpoint I believe this would be the simplest solution. In spring you can add RequestHeader and RequestBody annotations to method arguments to have them setup to be used.
Of course you can map RequestBody directly to some POJO instead of using a map but just as an example.
Let me know if this is what you were looking for !
#TryHard, You're using spring boot then following way is more preferable for you,
#RestController
public class SampleController {
#RequestMapping("/get-header-data")
public ResponseEntity<?> sampleEndpoint(HttpServletRequest request) {
// request object comes with various in-built methods use as per your requirement.
request.getHeader("<key>");
}
}
you can get header with your code but need apply some changes.
private String getRequest() throws Exception {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs != null) {
HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();
return request ;
}
throw new IllegalArgumentException("Request must not be null!");
}
after you can extract header info from request. For example if you want get Accept-Encoding
String headerEncoding = getRequest().getHeader("Accept-Encoding");
obliviusly you don't use this approce if not necessary.
If you want exract the body NOT use this solution

Customization soap response

i am working on web services project with Apache CXF.
I wan't to handle exception and customize response:
public class FaultInterceptor extends
AbstractSoapInterceptor {
public FaultInterceptor() {
super(Phase.MARSHAL);
}
public void handleMessage(SoapMessage soapMessage) throws Fault {
Fault fault = (Fault) soapMessage.getContent(Exception.class);
QName faultCode = new QName("11111");
fault.setFaultCode(faultCode);
So here is what i get in the response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:11111</faultcode>
<faultstring>Message</faultstring>
</soap:Fault>
</soap:Body>
How can i remove the text "soap:" and let only 11111?
Please help me and thanks in advance
To customize the SOAP response, you can implement either one of these:
JAX-WS handler;
CXF interceptor (extend AbstractSoapInterceptor);
Servlet Filter.
--EDIT 2019-02-20--
According to Javadoc (and SOAP spec), the faultcode must be in the form "prefix:localname" where "prefix" is the prefix of a declared XML namespace in your XML, or you can have no prefix - that you want - if it is declared as the default namespace, e.g. with xmlns="my-custom-faultcode-namespace-uri" somewhere, e.g. in the soap:Envelope element. So one way - not sure it's the easiest but it is SOAP standard compliant - consists to:
1) Make up your own custom namespace for this faultcode
2) Try changing the QName with empty string as namespace prefix:
QName faultCode = new QName("my-custom-faultcode-namespace-uri", "11111", "");
If this is not enough (I would be surprised it is that simple), you may have force CXF to use your custom namespace as the default (without prefix). According to this post, to customize namespaces and prefixes on the soap enveloppe in CXF, you change the Map in the jaxws 'soap.env.ns.map' property.
I was looking for the exactly same thing since migrating an old system to behave exactly the same.
I came up with the following Solution.
class SoapFaultEndpointInterceptor extends EndpointInterceptorAdapter
{
private static final Pattern SOAP_CODE_FAULT_SPLITTER = Pattern.compile(":");
#Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception
{
SaajSoapMessage soapResponse = (SaajSoapMessage) messageContext.getResponse();
modifySoapFaultCode(soapResponse);
return super.handleFault(messageContext, endpoint);
}
private void modifySoapFaultCode(SaajSoapMessage soapResponse)
{
try {
SOAPMessage soapMessage = soapResponse.getSaajMessage();
SOAPBody body = soapMessage.getSOAPBody();
SOAPFault soapFault = body.getFault();
modifyFaultCodeIfPresent(soapFault);
} catch (SOAPException e) {
throw new SoapModifiyingException("Modifying faultcode did not work properly.", e);
}
}
private void modifyFaultCodeIfPresent(SOAPFault fault)
{
if (fault != null) {
String newFaultCode = cleanFaultCode(fault.getFaultCode());
fault.setFaultCode(newFaultCode);
}
}
private String cleanFaultCode(String oldFaultCode)
{
String[] cleanFaultCode = SOAP_CODE_FAULT_SPLITTER.split(oldFaultCode);
Assert.isTrue(cleanFaultCode.length == 2, "No proper faultcode provided!");
return cleanFaultCode[1].trim();
}
And by adding SoapFaultEndpointInterceptor to your Interceptor, it should work.
#EnableWs
#Configuration
public class SoapServerConfig extends WsConfigurerAdapter
{
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors)
{
interceptors.add(new SoapFaultEndpointInterceptor());
}
}

How to retrieve information about certificate in WebServiceTemplate?

I want to retrieve Common Name (CN) property from client certificate in SOAP communication. I'm using Spring WebServiceTemplate to create my webservice endpoint. I have already implemented WS mutual authentication following the example.
Is there any solution to obtain certificate details from client request by means of WebServiceTemplate or some other library?
Fortunately, I have managed to figure it out!
Spring WS provides very convenient way to retrieve the X509Certificate.
Normally, You have an endpoint like this:
#Endpoint
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";
...
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
#ResponsePayload
public GetCountryResponse getCountry(#RequestPayload GetCountryRequest request) {
//method body here
return response;
}
}
However, Spring allows to add additional parameters the method annotated as #PayloadRoot. It can be a MessageContext instance.
public GetCountryResponse getCountry(#RequestPayload MessageContext context, #RequestPayload GetCountryRequest request)`
Then You will be able to obtain the wsse:Security header as follows:
WebServiceMessage webServiceMessageRequest = context.getRequest();
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) webServiceMessageRequest;
SOAPMessage doc = saajSoapMessage.getSaajMessage();
Element elem = WSSecurityUtil.getSecurityHeader(doc.getSOAPPart(), "");
Now get the right content of BinarySecurityToken tag:
String binarySecurityToken = elem.getElementsByTagName("BinarySecurityToken").item(0).getTextContent();
At the end, you should recreate the X509Certificate by passing binarySecurityToken as its constructor parameter. Later You can extract CN by many different ways for example by means of LDAP utlis.
There is another way.
Create AbstractSoapInterceptor with this body :
private final static QName SECURITY_QNAME = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "");
private static CertificateFactory certFactory;
public xxx() throws CertificateException {
super(Phase.PRE_PROTOCOL);
certFactory = CertificateFactory.getInstance("X.509");
}
#SneakyThrows
#Override
public void handleMessage(SoapMessage message) throws Fault {
SoapHeader header = (SoapHeader) message.getHeader(SECURITY_QNAME);
Node binarySignatureTag = ((Element) header.getObject()).getFirstChild();
BinarySecurity token = new X509Security((Element) binarySignatureTag, new BSPEnforcer());
InputStream in = new ByteArrayInputStream(token.getToken());
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
}
Register it in configuration of endpoint :
#Bean
public Endpoint endpoint() throws CertificateException {
EndpointImpl endpoint = new EndpointImpl(springBus(), xxxPortType());
endpoint.setServiceName(xxxService().getServiceName());
endpoint.publish("/xxxx");
endpoint.getInInterceptors().add(new xxx());
return endpoint;
}

Spring gives HTTP Status 400 when receiving a soap request having an xml declaration with special character escape slashes

I am receiving an xml post request from my vendor having a declaration of this format.
<?xml version=\"1.0\" encoding=\"utf-8\"?>
With this type of xml declarion (I am using Spring MVC with JAXB) I am getting the HTTP Status 400 error which states that "The request sent by the client was syntactically incorrect." I tried to post the same request to my site using postman and i get the very same error.
But on changing the xml declarion by removing all the backslashes( see below)
<?xml version="1.0" encoding="utf-8"?>
the error vanishes and i get the correct response with HTTP Status 200, Ok.
My question is how can i intercept this request and modify the xml declaration by removing the forward slashes (My vendor does not comply with modify this from their end).
Below is the sample of my controller
#RequestMapping(method = {RequestMethod.GET,RequestMethod.POST}, value ="/listeningurl", consumes = "application/xml", produces = "application/xml")
public ResponseObject lodgementNotifications(#RequestBody RequesObject reqObject)
{
//do stuffs with reqObject;
// Initialize ResponseObject
return responseObject
}
Thanks for the help.
You can extends the HandlerInterceptorAdapter which is :
Abstract adapter class for the AsyncHandlerInterceptor interface, for
simplified implementation of pre-only/post-only interceptors.
#Component
public class MyHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// then access your request body (xml content) to update it
// see link bellow for how to retrieve an xml content from the HttpServletRequest
return super.preHandle(request, response, handler);
}
}
After that you override the addInteceptors of WebMvcConfigurerAdapter by creating a custom class that extends from WebMvcConfigurerAdapter :
#Configuration
public class CustomWebMvc extends WebMvcConfigurerAdapter {
#Autowired
MyHandlerInterceptor myHandlerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myHandlerInterceptor);
super.addInterceptors(registry);
}
}
To know how to retrieve the xml content from your HttpServletRequest read this Get Posted XML from HttpServletRequest Object
Edit
If your goal is simply to retrieve the http body ( xml content ) and then do what ever you want with it inside your controller you can simply inject an InputStream or a Reader in the #RequestMapping handler method (which is your controller method) like so :
#PostMapping(value = "/topics/xml", consumes = "application/xml")
public void getXmlRequest(InputStream data) throws IOException {
String requestBody = IOUtils.toString(data);
// format the xml and do something with it
}
Spring web doc : Handler Methods :
#RequestMapping handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values.
I was able to resolve the issue. I had to receive the xml request in String format, removed the backslashes then unmarshalled it into its corresponding object. Thanks to #Harry Coder for the hints. Here is the solution that worked for me.
#RequestMapping(method = {RequestMethod.GET,RequestMethod.POST}, value ="/listeningurl", consumes = "application/xml", produces = "application/xml")
public ResponseObject lodgementNotifications(#RequestBody String reqObjectXMLStr)
{
//Replace the backslashes from the xml string
String cleanReqObjectXMLStr = reqObjectXMLStr.replaceAll("\\\\", "");
//Unmarshal the string into the corresponding object using JAXB library
RequestObject reqObject = JAXB.unmarshal(new StringReader(cleanReqObjectXMLStr), RequestObject.class);
//do stuffs with reqObject;
// Initialize and set ResponseObject
return responseObject
}

HttpServletRequest is coming as null in Apache cxf interceptor

I need to match request along with its corresponding response so i am trying to take session id for matching each request along with its response.
I am using Phase.PRE_STREAM in my constructor.
I am trying to take HttpServletRequest and session id as below in my interceptor
public void handleMessage(Message msg) {
HttpServletRequest req = (HttpServletRequest)msg.get("HTTP.REQUEST");
}
But i am getting null value. Could someone tell me how to take HttpServletRequest in apache cxf?
Do i need to set session id while creating client.I create my client as below
JAXRSClientFactoryBean sf = new JAXRSClientFactoryBean();
sf.setResourceClass(CustomerService.class);
sf.setAddress("http://localhost:9000/");
BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory factory = new JAXRSBindingFactory();
factory.setBus(sf.getBus());
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
CustomerService service = sf.create(CustomerService.class);
WebClient wc = sf.createWebClient();
Implement SOAP Handler like so:
public class SOAPRequestHandler implements SOAPHandler<SOAPMessageContext>
And implement your handlers like so:
public boolean handleMessage(SOAPMessageContext msgContext)
{
HttpServletRequest request = (HttpServletRequest) msgContext.get("HTTP.REQUEST");
...
}

Categories

Resources