Basic HTTP verification through CXF interceptor not working

I'm having trouble setting the HTTP authorization header to request a web service 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 via MyHTTPAuthInterceptor as follows:

 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())))); } 

If the username and both are set to 'test', everything looks fine in the logs:

 Headers: {SOAPAction=[""], Accept=[*/*], Authorization=[Basic dGVzdDp0ZXN0]} 

However, the server returns HTTP 401: Unauthorized.

Not knowing what was going wrong, I took a completely different approach by changing the factory code of the web service client. I added the basic authorization policy for the client channel as follows:

 HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy(); authorizationPolicy.setUserName("test"); authorizationPolicy.setPassword("test"); authorizationPolicy.setAuthorizationType("Basic"); httpConduit.setAuthorization(authorizationPolicy); 

Re-checked my setup, same log (different order):

 Headers: {SOAPAction=[""], Authorization=[Basic dGVzdDp0ZXN0], Accept=[*/*]} 

Now the server response is 200 OK!

A problem that you could solve, but the second approach does not work for me. My application is an environment with several tenants, with different names and passwords. In the second approach, I cannot reuse my client.

How can I make my interceptor work correctly? Am I connecting to the wrong phase? Does the order of the headers mean? If so, how to change it?

+7
source share
2 answers

I have almost the same setup as yours, but I put my interceptor in the PRE_PROTOCOL phase. So far I have not experienced any problems. You can try this.

I think POST_PROTOCOL is too late, because too much has already been written to the stream.

+5
source

If you are looking for externalization of the client and the best approach to authentication is setting httpConduit in the context of spring.

  **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 specifying the SOAP address in the name attribute. which you 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) the name attribute must 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> 
+3
source

All Articles