I'm having some trouble setting the HTTP Authorization header for a web service request using Apache CXF. I have my client setup through spring:
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean id="myHTTPAuthInterceptor" class="my.app.MyHTTPAuthInterceptor" autowire="constructor" />
<bean id="webServiceFactory" class="my.app.WebServiceFactory">
<property name="wsdlLocation" value="classpath:/my/app/webservice.wsdl" />
<property name="serviceURL">
<jee:jndi-lookup jndi-name="webservice/url" />
</property>
<property name="inInterceptors">
<list>
<ref bean="loggingInInterceptor" />
</list>
</property>
<property name="outInterceptors">
<list>
<ref bean="loggingOutInterceptor" />
<ref bean="myHTTPAuthInterceptor" />
</list>
</property>
</bean>
<bean id="myWebService" factory-bean="webServiceFactory" factory-method="getInstance" />
Headers are set through MyHTTPAuthInterceptor like this:
public MyHTTPAuthInterceptor(ConfigDao configDao)
{
super(Phase.POST_PROTOCOL);
this.configDao = configDao;
}
#Override
public void handleMessage(Message message) throws Fault
{
Map<String, List<?>> headers = (Map<String, List<?>>) message.get(Message.PROTOCOL_HEADERS);
String authString = configDao.getUsername() + ":" + config.getPassword();
headers.put("Authorization", Collections.singletonList("Basic " + new String(Base64.encodeBase64(authString.getBytes()))));
}
With username and both set to 'test', everything seems to be OK in the logs:
Headers: {SOAPAction=[""], Accept=[*/*], Authorization=[Basic dGVzdDp0ZXN0]}
However, the server returns a HTTP 401: Unauthorized.
Not knowing what's going wrong, I took a whole other approach by changing my web service client factory code. I added a basic authorization policy to the client's conduit like this:
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy();
authorizationPolicy.setUserName("test");
authorizationPolicy.setPassword("test");
authorizationPolicy.setAuthorizationType("Basic");
httpConduit.setAuthorization(authorizationPolicy);
Tested my setup again, same log (different order though):
Headers: {SOAPAction=[""], Authorization=[Basic dGVzdDp0ZXN0], Accept=[*/*]}
Now the server's response is 200 OK!
Problem solved you might think, but the second approach doesn't really work for me. My application is a multi-tenant environment, all with different username and password. With the second approach I cannot reuse my client.
How can I get my interceptor to work correctly? Am I plugging into the wrong phase? Does the order of the headers matter? If so, how do I change it?
I have almost exactly the same setup as yours but I am putting my interceptor in the PRE_PROTOCOL phase. So far, I have not experienced any problem. You might try that.
I think POST_PROTOCOL is just too late because too much has already been written to the stream.
If you are looking to externalize the client and authentication best approach is to setup httpConduit in spring context..
**in your spring context file...**
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
...
<bean id="properties" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="locations">
<util:list>
<value>file:${config.dir}/application.properties</value>
</util:list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
...
<jaxws:client id="serviceClient" serviceClass="com.your.ServiceClass" address="${webservice.soap.address}" >
<jaxws:inInterceptors>
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:outInterceptors>
</jaxws:client>
...
applicaiton.properties
---------------------
webservices.http.auth.username=userName
webservices.http.auth.password=Password
webservice.soap.address=https://your.service.url/services/service
a) by mentioning the SOAP Address in the name attribute. which your can find in your WSDL
Ex: if in your WSDL..
<wsdl-definitions ... targetNamespace="http://your.target.namespace.com/" ...>
...
<wsdl:port binding="tns:YourServiceSoapBinding"
name="YourServiceImplPort">
<soap:address location="https://your.service.url/services/service" />
Then
...
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xmlns:sec="http://cxf.apache.org/configuration/security"
...
<http-conf:conduit name="https://your.service.url/services/service">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>
Or
b) name attribute should be {targetNamespace}portName.http_conduit
<http-conf:conduit name="{http://your.target.namespace.com/}YourServiceImplPort.http_conduit">
<http-conf:authorization>
<sec:UserName>${webservices.http.auth.username}</sec:UserName>
<sec:Password>${webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>Basic</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>
Related
My project is using jms+ibmmq to send a message out. it's working properly, and I'm able to push a message to the outbound queue, and then receive a response from the inbound queue. Recently, a client asks if we can send out an alert email if unable to get a response within like 2 min. I checked the spring integration messaging related document, seems gatway can implement it? but still can't get an idea how to use it. can someone help me out?
my current xml configuration
<integration:service-activator id="serviceActivator1" input-channel="inputChannel"
ref="messageProcessService" method="callMsgProcessor" output-channel="requestChannel" />
<bean id="ibmConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType">
<util:constant static-field="com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT"/>
</property>
<property name="hostName" value="${hostName}"/>
<property name="queueManager" value="${queue-manager}"/>
<property name="channel" value="${channel}"/>
<property name="port" value="${port}"/>
</bean>
</property>
<property name="sessionCacheSize" value="5"/>
</bean>
<bean id="requestQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${request-queue}"/>
<property name="targetClient">
<util:constant static-field="com.ibm.msg.client.wmq.WMQConstants.WMQ_CLIENT_NONJMS_MQ"/>
</property>
</bean>
<jms:outbound-channel-adapter id="request.queue.adapter" connection-factory="ibmConnectionFactory"
destination="requestQueue" channel="requestChannel"/>
<integration:channel id="requestChannel"/>
<bean id="responseQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${response-queue}"/>
</bean>
<jms:message-driven-channel-adapter id="responseJMSAdapater"
destination="responseQueue"
channel="responseChannel"
connection-factory="ibmConnectionFactory"
error-channel="errorChannel"
acknowledge="transacted"
/>
<integration:channel id="responseChannel">
<integration:queue capacity="500"/>
<integration:interceptors>
<integration:wire-tap channel="logger"/>
</integration:interceptors>
</integration:channel>
if add a gateway, it looks like
<integration:gateway id="serviceAdaptedGateway" service-interface="com.mycompany.service.gatewayServiceInterface"
default-request-channel="requestChannel"
default-reply-channel="responseChannel"
default-reply-timeout="120000"/>
my question is
seems timeout not working, nothing happen even the waiting time exceed the timeout
although the gateway provide timeout property, where I can add my business logic like send email alert?
We use Apache Camel's camel-http component to integrate with HTTP endpoints, HttpConnectionManagerParams is used to configure defaultconnectionsPerHost and maxTotalConnections.
<bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams" id="MyHttpConnectionManagerParams">
<property name="defaultMaxConnectionsPerHost" value="20"/>
<property name="maxTotalConnections" value="200"/>
</bean>
Above parameters takes effect only if the endpoint URL is over HTTP, same configuration becomes void and default HttpConnectionManager takes effect when endpoint is over HTTPS.
Is there something to be additionally configured for HTTPS url?
Adding below beans has solved works for me.
Agreed there is no component named HTTPS in Camel but things are working with below configuration both in older and newer versions of Apache Camel.
<bean class="org.apache.camel.component.http.HttpComponent" id="http">
<property name="camelContext" ref="myCamelContext"/>
<property name="httpConnectionManager" ref="MyHttpConnectionManager"/>
</bean>
<bean class="org.apache.camel.component.http.HttpComponent" id="https">
<property name="camelContext" ref="myCamelContext"/>
<property name="httpConnectionManager" ref="MyHttpConnectionManager"/>
</bean>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" id="MyHttpConnectionManager">
<property name="params" ref="MyHttpConnectionManagerParams"/>
</bean>
<bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams" id="MyHttpConnectionManagerParams">
<property name="defaultMaxConnectionsPerHost" value="100"/>
<property name="maxTotalConnections" value="500"/>
</bean>
I want to add basic authentication (http authentication) to SOAP web service from spring integration. I am following the approach below to authenticate:
<bean id="httpComponentsMessageSender"
class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<property name="credentials">
<bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg value="userName" />
<constructor-arg value="*******" />
</bean>
</property>
</bean>
<int-ws:outbound-gateway id="uniqueId"
request-channel="requestServiceChannel" reply-channel="replyChannel"
uri="end point url" message-sender="httpComponentsMessageSender"
marshaller="ServiceMarshaller" unmarshaller="ServiceMarshaller">
</int-ws:outbound-gateway>
But I am getting this error: org.springframework.ws.soap.client.SoapFaultClientException: Internal Error
Is there any way to authenticate soap web service by adding basic authentication to it?
i found the solution finally, here is the solution
<bean id="apacheHttpClientParams" class="org.apache.commons.httpclient.params.HttpClientParams">
<property name="authenticationPreemptive" value="true" />
<property name="connectionManagerClass"
value="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" />
</bean>
<bean id="apacheHttpClient" class="org.apache.commons.httpclient.HttpClient">
<constructor-arg ref="apacheHttpClientParams" />
</bean>
<bean id="credentials"
class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg value="userName" />
<constructor-arg value="password" />
</bean>
<bean id="authentication"
class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<constructor-arg ref="apacheHttpClient"></constructor-arg>
<property name="credentials" ref="credentials" />
</bean>
I have a project using apache camel, and I have to consume a WS ..
.setHeader(HttpHeaders.AUTHORIZATION, constant("Basic {base64"))
.to("spring-ws:http://myhost.com.br?soapAction=myAction")
And I get the follow error : org.springframework.ws.client.WebServiceTransportException: Unauthorized [401]
If I send using soap-ui just with the header, it's work well.
Any ideas? Thanks a lot.
From http://people.apache.org/~dkulp/camel/spring-web-services.html
A custom message sender or factory in the registry can be referenced like this:
from("direct:example")
.to("spring-ws:http://foo.com/bar?messageFactory=#messageFactory&messageSender=#messageSender")
Spring configuration:
<!-- authenticate using HTTP Basic Authentication -->
<bean id="messageSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<property name="credentials">
<bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg index="0" value="admin"/>
<constructor-arg index="1" value="secret"/>
</bean>
</property>
</bean>
<!-- force use of Sun SAAJ implementation, http://static.springsource.org/spring-ws/sites/1.5/faq.html#saaj-jboss -->
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="messageFactory">
<bean class="com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl"></bean>
</property>
</bean>
I am using a spring configuration like this to add a CustomHandler. It is working fine. As per documentation - customHandlerResolver is called once per proxy.
Here lies the issue. I need to add a dynamic security token header for each SOAP request and since the handler is called only once, my token expires after certain time, I am not able to set a refreshed token.
<bean id="myServicePort" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="org.my.myService" />
<property name="wsdlDocumentUrl" value="classpath:wsdl/mysoap.wsdl" />
<property name="namespaceUri" value="http://services.mycom.org" />
<property name="serviceName" value="OrderService" />
<property name="endpointAddress" ref="OrderEndPoint" />
<property name="handlerResolver" ref="customHandlerResolver"/>
</bean>
Have you tried using bean scope prototype.
<bean id="myServicePort" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean" scope="prototype">
<property name="serviceInterface" value="org.my.myService" />
<property name="wsdlDocumentUrl" value="classpath:wsdl/mysoap.wsdl" />
<property name="namespaceUri" value="http://services.mycom.org" />
<property name="serviceName" value="OrderService" />
<property name="endpointAddress" ref="OrderEndPoint" />
<property name="handlerResolver" ref="customHandlerResolver"/>
As I said HandlerResolver is called only once, no matter what the scope of the bean is. I used CXF - org.apache.cxf.jaxws.JaxWsProxyFactoryBean as I get more control on bean creation, unlike the above Spring proxy where Spring itself creates the proxy.
<bean id="proxyFactory"
class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="org.my.myService"/>
<property name="address" value="http://localhost:9002/HelloWorld"/>
</bean>
In my client code
//Set a handler
proxyFactory.setHandlers( Arrays.asList((Handler) new TokenHandler(Token)));
OrderService orderServicePort= (myService) proxyFactory.create();
//Call service method, as SOAP message has desired dynamic header
orderServicePort.getXXX()
This works perfectly and is less verbose than my initial spring config