I have developed a webservices using spring+ XSD+ Payload. I have a requirement of authenticating the request header with username and password coming in SOAP request header which i achieved with SOAPUI
I m able to generate the below header in the request
<soapenv:Envelope xmlns:jaxb="http://jaxb.miws.sg.com/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<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-C3092BFBAE5B212E93144378035575013">
<wsse:Username>User</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">test</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">CT1Fyo/g2WMaadE52bsnkg== </wsse:Nonce>
<wsu:Created>2015-10-02T10:05:55.750Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Now i want to validate the header elements for userName and Password.
Ex:
Case 1:
userName=User and Password=test //Authentication passed and give response Success
Case 2:
userName=User1 and Password=test1 //Authentication failed and give response Failure
Please help me to provide the suitable samples to achieve same.
Handlers in SOAP webservices (similar to Interceptors/Filters) can be used for the authentication purpose on the server side and then chaining the request further.
Please have a look at SOAPHandler to parse the header information from the payload and authenticating the username/password.
SOAP Handler at Server Side
Here are some steps to do that:
Implement a SOAPHandler class by writing a custom handleMessage method.
Within the handleMessage method, evaluate the context's MESSAGE_OUTBOUND_PROPERTY. If it is false (meaning it is an inbound message), then write code that introspects the context.getMessage(). There you can evaluate the MIME headers, the security headers & tokens and the body, to determine if you need to reject the authentication credential. If you do, return false at the end of the method.
Add the SoapHander you created to the service's Handler chain.
Example of a SOAPHandler:
public class MyCustomSoapHandler implements SOAPHandler<SOAPMessageContext>
{
public Set<QName> getHeaders()
{
return Collections.emptySet();
}
public boolean handleMessage(SOAPMessageContext messageContext)
{
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
//This is for handling messages going out of the conduit
} else {
//Here is where you want to authenticate
}
return true; //return false if do not want to proceed to the next handler in the chain
}
public boolean handleFault(SOAPMessageContext messageContext)
{
return true;
}
public void close(MessageContext messageContext)
{
}
Here a starter template for your SOAPHandler that you need to add to your Service's handlerChain:
#WebService(name = "Handler", targetNamespace = "http://example.org")
#HandlerChain(file="handler-chain.xml")
public class HandlerWS
{
#Resource
WebServiceContext ctx;
#WebMethod()
public String getProperty(String propertyName)
{
return (String) ctx.getMessageContext().get(propertyName);
}
}
You'll also need to add the handler-chain.xml to your classpath:
examples.webservices.handler.Handler1
examples.webservices.handler.Handler2
For a complete guide, see Oracle's guide to creating SOAPHandlers
Related
I'm trying to implement a soap service consumer in Java, using spring WebServiceGatewaySupport.
When I'm using curl to consume the service as below, it is giving proper response.
curl -d #request.xml -H 'SOAPAction:abc:mnEvent#DoAction' https://myhost.org/cd/doAction.jsp
I'm trying to implement the same using JAVA, by adding following HttpHeaders in a template class inheriting from WebServiceGatewaySupport
public O callWebService(String url, I request) {
return (O) getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
TransportContext transportContext = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) transportContext.getConnection();
connection.getHttpPost().addHeader("SOAPAction", "abc:mnEvent#DoAction");
}
});
}
With this code, I'm getting an error message like below.
SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'.
What do I miss here when moving curl command to JAVA?
The error message SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'. indicates, there are two soap actions in the request.
Explicit SoapAction added in HttpHeader
Implicit SoapAction in SoapMessage
To avoid this issue, we need to remove the soapAction from header and set it in SoapMessage.
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
soapMessage.setSoapAction("abc:mnEvent#DoAction");
I have task where I need to transform custom SOAP message. At the beginning I have to get this custom SOAP message, then I need to transform this message using XSLT, then process this message in my WebService. The reverse process is repeated.
I have XSLT file and method for transforming SOAP message, but I don't know where I need to call this method for transforming. How do I intercept SOAP message and where I have to do it? Because I have only class with one method (example below) and i don't understand how I can transform this message before processing in webservice.
#WebService
public class Calculator {
public String showCard(final CreditCard creditCard) {
return creditCard.toString();
}
}
Here is the technical mapping what I need to do.
How do I intercept SOAP message and where I have to do it?
You need a SoapHandler to capture the soap request before the execution of the bussiness logic of the endpoint, and other SoapHandler to transform the outbound response.
Define a SoapHandler class (example extracted from mkyong)
public class CalculatorSoapHandler implements SOAPHandler<SOAPMessageContext>{
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
//for response message only, true for outbound messages, false for inbound
if(!isRequest){
try{
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
//process....
}catch(SOAPException e){
System.err.println(e);
}catch(IOException e){
System.err.println(e);
}
}
//continue other handler chain
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
}
}
Create a soap handler XML file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-class>com.CalculatorSoapHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>
Attach SOAP Handler to Web Service using #HandlerChain
#WebService
#HandlerChain(file="handler-chain.xml")
public class Calculator {
If you use a jax-ws framework like CXF or spring-WS, check the documentation to see specific configuration
I will be extremely grateful if someone share his experience in solving the following problem.
I have a SOAP service in the JDK implementation (which is Metro, I believe).
For the logging purpose we need to extract the body of both the incoming request and generated response.
I try to fetch it by implementing a SOAPHandler on the server side.
I configure handler as a Spring bean.
All the examples I found essentially replicate the example from the Oracle documentation: https://docs.oracle.com/cd/E23943_01/web.1111/e13734/handlers.htm#WSADV170:
public boolean handleMessage(SOAPMessageContext messageContext)
{
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
System.out.println("** Response: "+messageContext.getMessage().toString());
return true;
}
Here one reads one of the Boolean properties of the SOAP message context which, as I see it, corresponds to either request or response.
But debugger in my experiments never enters the branch corresponding to response (else-branch). How is such handler supposed to trace both request and response?
I also wonder what message is read as messageContext.getMessage(): is it incoming (request) or outbound (response)
I wonder now is it possible indeed by implementing handleMessage() method to get access to both request and response?
Does a single handler intercepts both request and its response?
Did I misunderstand the example?
And ... SOAPHandler - is it a specific instance for every request (request-response pair)?
Thank you
Try this for SoapHandler:
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isRequest) {
//handle request
} else {
//handle response
}
And this for LogicalHandler:
Boolean outboundProperty = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
I used the Netbeans Web Service wizard to generate Java code given a WSDL. If I drag the web service method into a class, then it creates some Java code to call that web service (ex: SubmitApplication). I can see how to populate objects to send info to that web service, but the service also requires a security header with username/password.
There is a generated class called SecurityHeader that contains the username/password attributes. I can create this object with a valid username/password, but I cannot see how to pass that object or add it to the SubmitApplication call. How can the SecurityHeader be added to the SubmitApplication call?
Here is an example of what the SOAP request should look like:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<SecurityHeader xmlns="http://schemas.turss.com/BDS/1.0/">
<CreateTime>6/8/2012 8:32:59 PM</CreateTime>
<Owner>Sample_Owner</Owner>
<HashKey>Sample_Hash_Key</HashKey>
</SecurityHeader>
</soap:Header>
<soap:Body>
<SubmitApplication xmlns="http://schemas.turss.com/BDS/1.0/">
<newSearch>
<CurrentApplicant xmlns="http://schemas.turss.com/BDS/1.0/proxy">
<FirstName>Bob</FirstName>
<MiddleName />
<LastName>Smith</LastName>
<Suffix />
<BirthDate>1970-10-20T00:00:00</BirthDate>
<SSN />
<Address />
<City />
<State />
<PostalCode />
</CurrentApplicant>
<PermissiblePurpose xmlns="http://schemas.turss.com/BDS/1.0/proxy">TenantScreening</PermissiblePurpose>
</newSearch>
</SubmitApplication>
</soap:Body>
</soap:Envelope>
I found a sample code that add a token string to a soap security header. Here is the header form of the code below :
<TicketHeader>
<Ticket>OD01096347CCA</Ticket>
</TicketHeader>
The method to add the header to the message :
// Security token
String token;
// MyService and MySoapService are stubs generated from WSDL
MyService service = new MyService();
MyServiceSoap ep = service.getMyServiceSoap();
Binding binding = ((BindingProvider) ep).getBinding();
List handlers = binding.getHandlerChain();
handlers.add(new MySOAPHandler(token));
binding.setHandlerChain(handlers);
code of MySoapHandler :
public class MySOAPHandler implements SOAPHandler {
private String token;
public DHSOAPHandler(String token) {
this.token = token;
}
public boolean handleMessage(SOAPMessageContext messageContext) {
SOAPMessage
msg = messageContext.getMessage();
if ((Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)){
try {
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement el = header.addHeaderElement(envelope.createName("TicketHeader",
"", "http://ws.service.com/"));
el = el.addChildElement(envelope.createName("Ticket", "", "http://ws.service.com/"));
el.setValue(token);
msg.saveChanges();
}
catch (SOAPException e) {
return false;
}
}
return true;
}
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}
public void close(MessageContext messageContext){
}
// I'm not quite sure about what should this function do, but I guess something like this...
public Set getHeaders(){
Set headers = new HashSet();
headers.add(new QName("https://ws.service.com/", "TicketHeader"));
return headers;
}
}
I'm trying to create a generic web service that will always respond with "OK", regardless of the request's header or body contents. I can do this in Axis2 with a RawXMLInOutMessageReceiver, but I'd prefer to use JAX-WS (which I am completely new to) if at all possible. So far I've got a simple interface:
#WebService
public interface DummyService {
#WebMethod String processMessage(Object obj);
}
and a simple implementaion:
#WebService(endpointInterface = "com.dummyservice.DummyService")
public class DummyServiceImpl implements DummyService {
#Override
public String processMessage(Object obj) {
return "OK";
}
}
I can successfully publish the service with javax.xml.ws.Endpoint#publish(...), but when I hit it with a simple SOAP request, e.g.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<derp/>
</soapenv:Body>
</soapenv:Envelope>
I'm greeted with a SOAPFault stating Cannot find dispatch method for {}derp.
Is it even possible to create a generic/dumb web service that will ACK everything with JAX-WS? If so, could someone point me in the right direction?
EDIT
Thanks to the tip from McDowell, I was able to do this with a SOAPHandler:
public class DummySOAPHandler implements SOAPHandler {
#Override
public boolean handleMessage(MessageContext context) {
return process((SOAPMessageContext) context);
}
#Override
public boolean handleFault(MessageContext context) {
return process((SOAPMessageContext) context);
}
#Override
public void close(MessageContext context) { }
#Override
public Set<QName> getHeaders() {
return null;
}
private boolean process(SOAPMessageContext ctx) {
try {
SOAPMessage message = ctx.getMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPBody body = message.getSOAPBody();
if ((Boolean) ctx.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {
Iterator<SOAPElement> bodyChildren = body.getChildElements();
while (bodyChildren.hasNext()) {
SOAPElement child = bodyChildren.next();
child.detachNode();
}
body.addBodyElement(envelope.createName("OK"));
message.saveChanges();
}
} catch (SOAPException e) {
e.printStackTrace();
}
return true;
}
}
I expect your service is expecting something of the form:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dum="http://yournamespace/">
<soapenv:Header/>
<soapenv:Body>
<dum:processMessage>
<!-- xsd:anyType -->
</dum:processMessage>
</soapenv:Body>
</soapenv:Envelope>
Add ?WSDL to your endpoint and inspect the operation input XML type and the namespaces.
You might be able to do something with a logical handler (javadoc) to transform the incoming request to this form - I haven't tried.