I am currently creating a SOAP-Client in Java with help of Apache CXF.
I've generated the Service classes from a given WSDL and configure the client programmatically.(Just to make clear, that I'm not using Spring configuration).
The service I'm calling has the requirement that each Request I send, needs to be signed.
What I did so far is creating my client and add the WSS4JOutInterceptor in order to sign the message.
Client client = ClientProxy.getClient(soapService.getRawSoapInterface());
//Actually not sure if this is really needed?
QName signatureQName = new QName("http://www.w3.org/2000/09/xmldsig#", "Signature");
Map<String, Object> properties = new HashMap<String, Object>();
Map<QName, Object> processorMap = new HashMap<QName, Object>();
processorMap.put(WSSecurityEngine.SIGNATURE, signatureQName);
properties.put("wss4j.processor.map", processorMap);
properties.put(WSHandlerConstants.USER, "clientSignatureAlias");
properties.put(WSHandlerConstants.PW_CALLBACK_CLASS, MyPwCallback.class.getName());
properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
properties.put(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE);
properties.put(WSHandlerConstants.SIG_PROP_FILE, "client.properties");
properties.put(WSHandlerConstants.ENC_KEY_ID, "X509KeyIdentifier");
WSS4JOutInterceptor wssOutInterceptor = new WSS4JOutInterceptor(properties);
My client.properties contains:
org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin
org.apache.wss4j.crypto.merlin.keystore.type=jks
org.apache.wss4j.crypto.merlin.keystore.password=secret
org.apache.wss4j.crypto.merlin.keystore.alias=cert_sig
org.apache.wss4j.crypto.merlin.keystore.file=clientCerts.jks
So far so good, and each Message is getting signed.
Lets get to the issue:
The Problem is, that The interceptor is putting these Security Headers into the Soap-Request.
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<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" soap:mustUnderstand="1">
At first I don't them, second point is that the service I am calling doesn't know them and therefore is answering with an exception.
Currently I cannot find a way how to avoid this, any suggestions?
As far as I understood, WSS4J is not able to create an Enveloped Signature at all!
Therefore I moved into another direction. I used Apache Santuario in order to create a Signature for my message.
I used the Intercepor mechanism of CXF to create my own interceptor, an abstract class for this usecase is provided here: How To Modify The Raw XML message of an Outbound CXF Request? .
There I was able to call the Santuario STAX-API to create an valid signature, this is described very good in the following blog: http://coheigea.blogspot.ie/2014/03/apache-santuario-xml-security-for-java.html
Since I had some further modifications on the request, I was able to modify the raw String.
Thank god that SOAP is standardized protocoll and everybody is doing what he wants to...
Related
We use the Spring SAML Security Extension to implement SAML in our application. We now have the following problem:
One of our customers is providing a URL for their identity provider that contains a parameter. The metadata looks like this (heavily abbreviated for brevity):
<EntityDescriptor>
<IDPSSODescriptor>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://idp.example.com/login?parameter=value"/>
</IDPSSODescriptor>
</EntityDescriptor>
As can be seen, there is a parameter named "parameter" with a value "value". This parameter is not present in the generated redirect URL. I debugged a bit and found out that SAMLProcessorImpl gets the MessageEncoder from the binding (which is HTTPRedirectDeflateEncoder for HTTP redirect) and delegates encoding the message. The encoder in turn does the following in its buildRedirectURL method:
// endpointURL is https://idp.example.com/login?parameter=value here
URLBuilder urlBuilder = new URLBuilder(endpointURL);
List<Pair<String, String>> queryParams = urlBuilder.getQueryParams();
queryParams.clear(); // whoops
So for some reason, the parameters are stripped intentionally and unconditionally.
Why is this the case and how can I fix this in the most efficient way?
SAML Authentication Request should be only sent by trusted entities and with parameters which cannot be tampered with. Adding a parameter in addition to SAMLAuthnRequest encoded according to HTTP-Redirect binding will mean that a potential attacker can change the value as he/she pleases and IDP will not be able to detect such change - as the parameter will not be covered by digital signature.
SAML provides a mechanism for delivery of secured content in addition to request itself called relayState - and you can set it using WebSSOProfileOptions of Spring SAML.
The above is reason the parameters are cleared (at least I believe so, this logic comes from OpenSAML library which is not written by me), but of course in case you don't mind the security implications, the approach you found is just fine.
For now I solved the problem by extending HTTPRedirectDeflateEncoder, copying the code for buildRedirectURL and remove the queryParams.clear(). Because the URL is created by simple concatenation I explicitly removing all reserved SAML parameters (like SigAlg and the like) in order to avoid potential security issues. After that, it's a simple Spring configuration change to wire everything together.
I still don't know why the list is cleared in the first place, though.
I know I can add handlers(JAX WS) to an SEI using #HandlerChain
I know I can add interceptors(Apache CXF) to an SEI like this -
http://web-gmazza.rhcloud.com/blog/entry/jaxwshandlers-to-cxfinterceptors
I know I can add handlers to the Provider Interface using #HandlerChain-
https://docs.oracle.com/middleware/1213/wls/WSGET/jax-ws-soaphandlers.htm#WSGET3461
Question is :
Can I, and if so How,(the same way as SEI?) add Interceptors to the Provider interface?
Well, I figured out the answer to this specific question. You can add interceptors like this
ProviderImpl implementor = new ProviderImpl();
JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();
svrFactory.setAddress("http://localhost:9000/providerexample");
svrFactory.setServiceBean(implementor);
svrFactory.getInInterceptors().add(new LoggingInInterceptor());
svrFactory.getOutInterceptors().add(new LoggingOutInterceptor());
svrFactory.create();
But now the next problem : interceptors deal with SoapMessage(Apache CXF). Provider deals with SOAPMessage(JAXWS). So I can get the interceptors to log and all, but when I try to manipulate the SoapMessage, I have troubles. Still not sure if the reason is the incompatibility of these two classes( or whether the framework takes care of the interconversion) or the specific code I'm using there.
EDIT: There is no problem with the Interceptors, it was just some stupid errors I made.
I have two Java EE applications on two separate application servers. One of them already contains a working EJB. I want to be able to communicate with this EJB from the other application by using a JAX-WS webservice (the communication has to work between different application servers and different server versions, so remote EJB call is no option). It is no problem to expose the server api to the client application.
The server side is quite clear, adding #Webservice annotation seems to work quite well. But i wonder what is the best way to build the client: I don't really want to generate the client stub from the wsdl (which itself has been generated from the ejb code by the container in my case) and pack all these generated classes into the client ear - but this seems to be the only way i can make use of #WebServiceRef annotations.
The alternative to make a dynamic proxy myself with the help of the static methods of javax.xml.ws.Service (sth. like service=Service.create() and service.getPort()) is not recommended by the Spec and "container providers are not required to support managed Service Instances created using these methods".
But that is exactly sth. that I want to use:
Is there a way to get a dynamic proxy injected in my code, managed by the application server? Or is the only way to get a managed webservice client instance to be done with generated client stub classes?
Read JAX-WS 2.2 spec, Chapter 4: Client APIs.
1. Static Client Generation
Is really the simplest way to work with JAX-WS. From a web services perspective, the WSDL is the interface AND the connection properties. Even if you choose not to work with it physically, you still need to know it in a logical sense to make meaningful SOAP calls.
Note from JAX-WS spec: An Endpoint that uses the SOAP 1.1/HTTP binding MUST
make its contract available as a WSDL 1.1 document at the publishing address suffixed with ?WSDL or ?wsdl
2. Dynamic Client Programming
Is there a way to get a dynamic proxy injected in my code, managed by the application server?
This approach involves dynamic programming against the JAX-WS API to connect to a web service either with or without using WSDL. There's no way to just "inject" a dynamic proxy out of nowhere. You need to construct & configure one with the SEI's port URLs. The WSDL document is the standard place to store such configuration information, although it is possible to avoid it and to programmatically insert the info.
2A) Dynamic programming with WSDL:
javax.xml.ws.Service service = Service.create(
new URL("http://example.org/stocks.wsdl"),
new QName("http://example.org/stocks", "StockQuoteService"));
com.example.StockQuoteProvider proxy = service.getPort(portName,
com.example.StockQuoteProvider.class)
javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider)proxy;
Map<String,Object> context = bp.getRequestContext();
context.setProperty("javax.xml.ws.session.maintain", Boolean.TRUE);
proxy.getLastTradePrice("ACME");
Advantages over (1): can dynamically dynamically change the WSDL doc after app is deployed, provided such changes do not affect the java interface to client.
i.e. very little benefit to you. Your WSDL is static. Whilst you could point your client to <service endpoint URL>?wsdl to dynamically lookup, this means you need to manually configure <service endpoint URL> AND that leaves little else that can change in the SEI/WSDL without impacting your client logic.
2B) Dynamic programming without WSDL:
String endpointUrl = ...;
QName serviceName = new QName("http://example.org/wssample/echo/", "EchoService");
QName portName = new QName("http://example.org/wssample/echo/", "EchoServicePort");
/** Create a service and add at least one port to it. **/
Service service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
/** Create a Dispatch instance from a service.**/
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE);
/** Create SOAPMessage request. **/
// compose a request message
MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
// Create a message. This example works with the SOAPPART.
SOAPMessage request = mf.createMessage();
SOAPPart part = request.getSOAPPart();
// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();
// Construct the message payload.
SOAPElement operation = body.addChildElement("invoke", "ns1",
"http://com/ibm/was/wssample/echo/");
SOAPElement value = operation.addChildElement("arg0");
value.addTextNode("ping");
request.saveChanges();
/** Invoke the service endpoint. **/
SOAPMessage response = dispatch.invoke(request);
Advantage (not really): can eventually get it to carry out same behaviour as above.
Disadvantages: Complex programming. Non-standard configuration (outside of WSDL). Need to avoid hard-coding settings. Brittle to interface changes. Manually synchronising settings between server and client - easy to omit something, extremely difficult to debug.
Answer:
Go back to (1). Generate a client stub from the WSDL. Use it as an interface contract - it should be designed well and not change.
Then spend the time you save solving real problems... ;) ;)
I'm using wsdl2java generated classes and this code:
MyService f = new MyService();
MyServicePortType type = f.getMyServicePortType();
Each of these calls is taking up to 30 seconds. Why is that?
After hours of googling and tinkering the problem was in how scheme files were referenced:
although WSDL and XSD were locally stored there was still some referenced to w3.org that looked like this:
<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd" [...
<import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd" />
w3.org server was resposing super-slowly hence the slow initialization of my client.
I have changed the reference to local:
<import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd" />
I owe you a great debt since I have been struggling with this for days and your answer pointed me in the right direction.
Indeed w3.org URLs take ages to respond, however there is actually no reason why a SOAP client generated from a WSDL should still need to parse the WSDL at runtime.
In fact it does not, however default generated construstors force that.
I've discovered that it's possible to skip this by instantiating the service port in a different way and specify the service endpoint via request context as follows:
// creating the service this way passes null as the wsdlLocation, preventing the runtime resolution and parsing of the wsdl
Service service = ZefixService.create(ZefixService.SERVICE);
ZefixServicePortType zefixServicePort = service.getPort(ZefixServicePortType.class);
Map<String, Object> requestContext = ((BindingProvider) zefixServicePort).getRequestContext();
// because we create the service without the wsdl location, we need to specify the service base url ourselves
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, Configuration.get(Constants.API_BASE_URI_PROPERTY));
requestContext.put(BindingProvider.USERNAME_PROPERTY, Configuration.get(Constants.USER_PROPERTY));
requestContext.put(BindingProvider.PASSWORD_PROPERTY, Configuration.get(Constants.PASSWORD_PROPERTY));
return zefixServicePort;
I hope this turns useful for you and others in the future.
Thanks again
Can someone fill in the missing link in the code below?
First way:
The web service interface file is HappyService.
JaxWSProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInterceptors().add(new LoggingInInterceptor());
factory.getInterceptors().add(new LoggingOutInterceptor());
//MISSING LINK. Where does HappyService.class come from? I don't have it
factory.setServiceClass(HappyService.class);
factory.setAddress("http://......../happyService");
//Again how do I get HappyService?
HappyService client = (HappyService) factory.create();
Second way:
String UrlString = "Your WSDL URL";
String nameSpaceUri = "urn:Foo";
String serviceName = "MyHelloService";
String portName = "HelloIFPort";
URL helloWsdlUrl = new URL(UrlString);
ServiceFactory serviceFactory = ServiceFactory.newInstance();
Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(nameSpaceUri, serviceName));
//Where did dynamicproxy.HelloIF come from? This code won't compile as that file does not exist anywhere
dynamicproxy.HelloIF myProxy =
(dynamicproxy.HelloIF)
helloService.getPort(
new QName(nameSpaceUri, portName),
dynamicproxy.HelloIF.class);
System.out.println(myProxy.sayHello("Buzz"));
Anyone that has a clue as to where these interface classes come from and how they are generated please let me know. It looks like the only way I can do an web service invocation is by writing the SOAP request by hand and I really don't want to do that as it can get very large and error prone.
There are many tools that generate webservices Java classes from WSDL definition files.
You could try JAXB, which is the standard Java tool for this task.
An other possibility is Axis, which a level higher.
You need a SOAP library such as Apache Axis2. The library will include tools for generating Java classes from WSDLs. You would use that generated code to make the web service calls.
Based on your first sample I think you use the CXF framework.
This framework provides a task named wsdl2java that allows to generate classes from a WSDL file.
Once your classes are generated you can use them in your code to call the Web Service in an easy way without having to build the SOAP message by hand. It's CXF's job to do this.
I think it helps if you refer few basics of web-services in java
http://www.oracle.com/technetwork/java/index-jsp-137004.html
http://metro.java.net/