I sort of understand that I'm using Apache Axis 1.4 for querying a web service.
I was given a WSDL file, and that generated a XXXXImplServiceLocator class that extends org.apache.axis.client.Service and implements a generated interface that extends javax.xml.rpc.Service.
My "bussiness code" calls the getXXXXImplPort(URL) method on a new XXXXImplServiceLocator instance, and then calls:
((Stub) port).setTimeout(timeout);
((Stub) port).setUsername(username);
((Stub) port).setPassword(password);
Stub is org.apache.axis.client.Stub.
After setting those parameters, I simply call some getYYY() method, that represents the query to one of the services exposed by the remote service and gives me back a "domain" object with the results.
The problem is that I have to use preemptive authentication with this service. I've tried querying via soapUI, and I won't have any result unless I set the preemptive authentication mode.
I've tried googling how to set that mode, but found nothing. All they say about is Preemptive authentication with Apache HttpClient, that can be very well "embedded" or used by Axis, but I really didn't see how to mix this.
The only code I have that interacts with this ServiceLocator is the initial setup, and then just queries.
How can I set that preemptive authentication mode? Am I missing something? What am I not understanding so that I can't solve this issue?
Smells like a pretty trivial problem, but maybe the fact I don't understand this very well prevents me from solving the issue.
Thanks in advance, yeah :)
To force Apache Axis to use pre-emptive authentication, set the username and password like so:
import org.apache.axis.client.Call;
import org.apache.axis.client.Stub;
....
....
((Stub) client)._setProperty(Call.USERNAME_PROPERTY, "username");
((Stub) client)._setProperty(Call.PASSWORD_PROPERTY, "password");
That said, if you still want to use the Apache HttpClient as the transport, you can do so by setting the following in Axis client configuration file which in my case is client_config.wsdd:
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
.....
.....
<!-- use CommonsHTTPSender instead of the default HTTPSender -->
<!-- <transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender" /> -->
<transport name="http" pivot="java:org.apache.axis.transport.http.CommonsHTTPSender" />
.....
.....
</deployment>
I got the above from here.
You can use the Apache Rampart module to implement WS Security. It plugs in at the service level and will handle all things WS-Security like username/token, X.509 auth, etc.
Its not trivial, but its not that difficult either. We did it here to get an Axis server talking to an Oracle SOA server using WSS.
Related
I have a Spring MVC application secured with Spring Security. The majority of the application uses simple HTTP to save resources, but a small part processes more confidential information and requires an HTTPS channel.
Extract from the security-config.xml :
<sec:http authentication-manager-ref="authenticationManager" ... >
...
<sec:intercept-url pattern="/sec/**" requires-channel="https"/>
<sec:intercept-url pattern="/**" requires-channel="http"/>
</sec:http>
All worked fine until we decided to migrate it to the main server, where the application servers run behind reverse proxies. And as now HTTPS is processed by the reverse proxies the application server only sees HTTP requests, and disallows access to the /sec/** hierarchy.
After some research, I found that the proxies add a X-Forwarded-Proto: https header (*), but in Spring Security HttpServletRequest.isSecure() is used to determine the channel security offered (extract from SecureChannelProcessor javadoc).
How can I tell Spring Security that a X-Forwarded-Proto: https header is enough for a secure request?
I know I could report that part on proxies configuration, but the proxies administrator really does not like that solution, because there are many application behind the proxies and the configuration could grow to a non manageable state.
I an currently using Spring Security 3.2 with XML config, but I'm ready to accept answers based on Java config and/or more recent version.
(*) Of course, the proxies remove the header if it was present in incoming request, so the application can be confident in it.
Kind of a followup to NeilMcGuigan's answer that showed that the solution was servlet container side.
Tomcat is even better. There is a valve dedicated to masking the side effects of a reverse proxy. Extract from Tomcat documentation for Remote IP Valve:
Another feature of this valve is to replace the apparent scheme (http/https), server port and request.secure with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").
Example of the valve configuration :
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="192\.168\.0\.10|192\.168\.0\.11"
remoteIpHeader="x-forwarded-for" proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />
That way with no other configuration of the application itself, the call to Request.isSecure() will return true if the request contains a header field of X-Forwarded-Proto=https.
I had thought of two other possibilities, but definitively prefere that one :
use a filter active before Spring Security ChannelProcessingFilter to wrap the request with a HttpServletRequestWrapper overriding isSecure() to process a X-Forwarded-Proto header - need writing and testing the filter and the wrapper
use a Spring BeanPostProcessor to look for a ChannelProcessingFilter and manually inject a ChannelDecisionManager able to consider the X-Forwarded-Proto header - really too low level
Spring Boot makes it dead simple (at least with embedded Tomcat).
1. Add the following lines to your application.properties:
server.forward-headers-strategy=native
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
2. Do the following trick with your HttpSecurity configuration.
// final HttpSecurity http = ...
// Probably it will be in your `WebSecurityConfigurerAdapter.configure()`
http.requiresChannel()
.anyRequest().requiresSecure()
Source is Spring Boot reference guide
84.3 Enable HTTPS When Running behind a Proxy Server
Please also check the answer below for a specifics related to Spring Boot 2.2
If your site is HTTPS and you're running Apache Tomcat behind another system that's handling TLS termination, you can tell Tomcat to "pretend" that it's handling the TLS termination.
This makes request.isSecure() return true;
To do so, you need to add secure="true" to your Connector config in server.xml.
https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
See also the scheme attribute.
We are using Apache Camel with CXF endpoints to process our web service requests. What I'd like to be able to do is have my application be able to accept messages on any URL like:
http://localhost:9000/[anything]/MyService
http://localhost:9000/foo/MyService
http://localhost:9000/bar/MyService
I think I could make this a configurable setting, but I would much rather have it be completely dynamic and accept any path component before my service name. I've read about the Camel URL Rewrite Component and it seems like that may work, but it didn't feel like the right answer.
If it helps here is my endpoint configuration (with some details removed for brevity):
<cxf:cxfEndpoint id="MessageEndpoint"
serviceClass="MyClass"
serviceName="RespondingGateway_PortType"
address="{{web-service-url}}:{{port}}/subpath/MyService">
</cxf:cxfEndpoint>
What I'd like to be able to do is put something like /*/subpath/MyService in the address property to match on anything, but this doesn't work.
Background (a wee bit long):
A few years back I wrote for a customer a WCF service that is configured to use the basicHttpBinding with security mode = "Transport" with clientCredentialType = "Certificate", i.e. the clients are authenticating themselves using a (client) certificate. The service employs a custom AuthorizationManager that checks if the thumbprint of the incoming certificate is present in a predefined list of valid certificate thumbprints. If the incoming certificate is deemed valid, the operation is allowed to continue (if not, an exception is thrown).
The service has worked flawlessly for approximately four years and everyone's been happy. However, as so often happens requirements change and recently my customer has been approached by developers wishing to connect their applications to my customer's service. The only problem is that these developers are using some variant of Java as their preferred platform and now we are facing some serious problems. To make a long story short, no one has managed to get their Java implementation (e.g. Metro, Axis2) to work with the service as it is presently configured.
Last week we tried getting it to work with a client written in Java (Metro, JAX-WS) by changing the binding to the wsHttpBinding with security mode = "TransportWithMessageCredential" with Message clientCeredentialType = "UserName". I also added a custom UserNamePassWordValidatorType to the service credentials element in the config file.
I then commented out the custom AuthorizationManager since no certificate was coming from the client.
Lo' and behold, this time we got both SoapUI and the Java client proper to talk to the service.
(Btw, our service is self-hosted in a Windows Service)
Happy as can be, we decided to configure the service with two bindings, the existing basicHttpBinding that's been working without a glitch for a long time, and the newly tested wsHttpBinding. So we would have something like:
<services>
<service name="SuperDuperService" behaviorConfiguration="superDuperBehaviour">
<endpoint binding="basicHttpBinding" contract="ISuperDuperService" bindingConfiguration="SecureTransport"/>
<endpoint binding="wsHttpBinding" address="stws" contract="ISuperDuperService" bindingConfiguration="SecureTransportAndSoap"/>
<endpoint binding="mexHttpsBinding" address="mex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://<url + port>/SuperDuperService"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="SecureTransport" maxBufferSize="2065536" maxBufferPoolSize="524288" maxReceivedMessageSize="2065536">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
<readerQuotas maxDepth="32" maxStringContentLength="6553600" maxArrayLength="2065536"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
<wsHttpBinding>
<binding name="SecureTransportAndSoap">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="superDuperBehaviour">
<serviceCredentials>
<!-- The following element specifies the certificate use by this service for HTTPS (SSL) based transport security -->
<serviceCertificate findValue="<some identifier>"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType
="SupportClasses.CustomUserNameValidator,SupportClasses"/>
</serviceCredentials>
<!-- The following element specifies how we're authorizing based on the client certificates received -->
<serviceAuthorization serviceAuthorizationManagerType="SupportClasses.AuthorizationManager, SupportClasses"/>
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
So where is the problem you may ask? Well, see that <serviceBehavior> element? As you well may know this element applies globally to the service meaning that during run-time both the CustomUserNameValidator and the AuthorizationManager will be called. The latter will complain that there is no certificate present when a client calls the service using the wsHttpBinding!
Argh!
Alternative solutions.
So far, these are the alternative solutions I have come up with:
Alternative 1) Create another Windows Service hosting our WCF service on a different URL. Then both services will have a separate configuration.
Alternative 2) Create two service implementations hosted in the same Windows Service and expose them both in the element each with their own binding and serviceBehaviour
Alternative 3) Figure out if it is at all possible to keep the current configuration and have the CustomUserNameValidator and the AuthorizationManager coexist peacefully
Sorry for this long post, but I needed to be thorough when providing the background for my question(s).
Question 1) Has anyone gotten WCF to work with Java clients using nontrivial configurations?
Question 2) Has anyone got a suggestion on how to solve alternative 3? (if at all possible)
Question 3) Which one, if any, of the above alternatives, would you recommend?
Question 4) Is there other alternatives you know of that I haven't thought of?
For the record, I've looked into the WCF interoperability tool, but I can't really see how it can help us. If anyone reading this has had good results using the interoperability "wizard",
please let me know.
Thanks in advance.
--norgie
I would start by adding a second element in //services and configure that with a new #bindingConfiguration attribute and a distinct #address attribute. I think that will be simpler that alternatives 1, 2, or 3; or maybe it is alternative 3, I can't tell.
Web services exist to provide interoperation that is language-, vendor-, platform-, and vendor-neutral. WCF and Java interoperate in real world solutions every day.
Have you looked at this series on WCF and Java interoperation?
Also, it sounds like you would benefit from using some diagnostics tools that will let you see the different between the .NET/WCF client message and Java client messages. Use Fiddler or some other sniffer utility to see the messages on the wire. Turn on WCF tracing to see what WCF does once it receives the message.
Background:
I need to consume an existing web service (SOAP over http) that has a couple of issues:
1) The wsdl on the server doesn't even resemble the web service as described in their documentation, which includes a completely different wsdl file
2) The wsdl file provided with their documentation seems to come close to describing the web service on the server, but when I generated java client code using cxf and used it to access the web service, cxf throws exceptions like the following
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://us-labs.companyxyz.com/", local:"searchResponse"). Expected elements are <{http://companyxyz.com/xyz-xml/2.0/}searchResponse>
... 33 more
I'm no SOAP expert, but assuming this means the namespaces in their response don't match those defined in the wsdl.
Since my application is written in java, I was able to connect and get a response using commons http client and a handcrafted SOAP request, so worst case I can fall back to that and parse the response to get what I need.
My questions:
Did I interpret the exception correctly?
If no: any suggestions on how I can debug this?
If yes: can anyone suggest better alternatives to handcrafting http requests and parsing xml by hand? (Getting correct wsdl is, unfortunately, not an option)
Thanks in advance.
Most likely. The response is using the namespace "http://us-labs.companyxyz.com/", but in the WSDL, the same element is declared with namespace "http://companyxyz.com/xyz-xml/2.0/".
I'm not familiar with CXF, but other SOAP frameworks usually offer some kind of logging capabilities. It would probably help you if the SOAP requests and responses are logged somewhere for more specific analysis.
Why is it not an option to get a correct WSDL? If you really are able to "handcraft" correct SOAP requests and expect to be able to "handparse" the responses, you should be able to write the WSDL yourself as well. Of course, the WSDL should be provided to you by the service operator, but if you mean that noone is able to provide you with a correct WSDL, I would consider writing it myself instead of creating and parsing the SOAP messages manually.
I think you interpreted the exception correctly - the namespace is different than expected.
It is also not really unexpected. it is a fact of life that vendor supplied wsdls are not always correct. We actually write our own WSDLs and XSDs for vendor applications for just that reason.
You can use your own WSDL even run-time. There are some SO questions on that, here and here.
You could also have a look here. I haven't tried it, but it could work.
We actually extend the generated service and create a port supplying a WSDL located on the classpath using the JaxWS Service constructor. That works fine for us.
We debug CXF by dumping the incoming and outgoing messages. There seem to be quite a lot of methods to do just that. We use either a proxy between de web service and our client, or recently a cxf.xml file somewhere. Using a -D flag we temprarily configure this.
-Dcxf.config.file=/home/me/cxf-debug.xml
and cxf-debug.xml contains something like:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>
</beans>
http://cxf.apache.org/docs/debugging-and-logging.html
Both responses suggested the same basic approach, which turned out to be the correct one.
I fixed the provided wsdl to make it match the web service, and I was able to use cxf, which saved me a lot of hand coding.
The main problem with their wsdl was in fact a namespace issue. The essence of the problem was as follows: their wsdl defined two namespaces, both of which have a "searchResponse" element.
{http://us-labs.companyxyz.com/}searchResponse
was defined in the wsdl to contain 0 or more
{http://companyxyz.com/xyz-xml/2.0/}searchResponse
But in their response the nested searchResponse wasn't qualified by {http://companyxyz.com/xyz-xml/2.0/} so cxf interpreted it as a {http://us-labs.companyxyz.com/}searchResponse
I fixed it by introducing a new type.
Thanks to both responders.
Does anyone know of a really simple way of publishing Java methods as web services? I don't really want the overhead of using Tomcat or Jetty or any of the other container frameworks.
Scenario: I've got a set of Java methods in a service type application that I want to access from other machines on the local LAN.
Well, Tomcat or Jetty may be overkill for publishing just some methods as a web service. But on the other hand its not too complicated and they do the job, so why not?
I had a similar problem not too long ago and used a Tomcat together with Axis2. Just download Tomcat, unpack it, deploy the Axis2 WAR. To publish a webservice, there are several aproaches, the one I took is probably one of the easiest:
Just build your application as usual and annotate the web service class and methods with the appropriate annotaions from javax.jws.*. Package everything into a jar. Create a service.xml in the META-INF directory of your jar file and put this into it:
<service name="name of the service" scope="<one of request, session or application>">
<description>
optional description of your service
</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass" locked="false">put here the fully qualified name of your service class (e.g. x.y.z.FooService)</parameter>
</service>
Rename the .jar to .aar and put it into the /webapps/axis2/WEB-INF/services/ directory. Start tomcat and the service will be deployed. You can check if it is running by visiting the axis2 page (http://localhost:8080/axis2/). There you will see which services are deployed and which methods are exported. Also you can get the WSDL url there to connect to your service.
Read http://ws.apache.org/axis2/1_4_1/contents.html for more about using Axis2. The approach I described here is not found exactly like this in the docs, but it works very well.
Update: If you just want to provide web services and really don't need any of the other features of Tomcat (e.g. serving of plain old web pages, jsps or other stuff), you can also use the Axis2 standalone server. But except for the setup part it doesn't change anything I described.
I've written a slightly more detailed version of this, which can be found at: http://www.slashslash.de/lang/en/2008/10/java-webservices-mit-apache-tomcat-und-axis2/ (don't let the German in URL irritate you, it's written in English)
Web services depend on HTTP. You might not want tomcat or Jetty. In that case, you have to implement HTTP yourself.
Erhm. Why not just use RMI?
Jetty's pretty lightweight. Otherwise, I think XML-RPC is your only sensible option.
The simplier solution than the one that Simon has discribed, ist to use the tools that alrady do that. If you use eclipse you could use http://ws.apache.org/axis2/tools/1_2/eclipse/servicearchiver-plugin.html
to generate the aar file.