Search code examples
springcxfinterceptor

Unable to set Basic Auth header dynamically via HandlerResolver inside container


We have a requirement to set Basic Auth Authorization header to all outbound SOAP calls. Our J2EE application uses spring acting as the SOAP client.

<bean id="myServices"
    class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface"
        value="training.service.mybusinessservices.myBusinessServicesPort" />
    <property name="wsdlDocumentUrl" value="${my.service.wsdlDocumentUrl}" />
    <property name="endpointAddress" value="${my.service.endpoint}" />
    <property name="namespaceUri"
        value="http://training.org/myBusinessServices" />
    <property name="serviceName" value="myBusinessServices" />
    <property name="portName" value="myBusinessServices" />
    <property name="lookupServiceOnStartup" value="false" />
    <property name="handlerResolver" ref="serviceSecurityHandler" />
</bean>

Our requirement is to obtain the username & password from a central store based on the serviceName that we invoke. Hence the approach was to use handlerResolver to set the http header via interceptor (vs using JaxWsProxy.... username and password properties)

Our handlerResolver interceptor implementation

@Override
public boolean handleMessage(SOAPMessageContext context) {
    logger.debug("handleMessage()-start");
    boolean isOutBound = true;

    try {

        Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if(context.getMessage() !=null && outboundProperty.booleanValue()) {

            MessageLogger.logMessage(AuthConstants.HANDLE_MESSAGE, "Context not null " + context.getMessage(),AuthConstants.DEBUG, null);

            String sourceAppID = getValueByTagName(context,AuthConstants.SOURCE_APPID);
            String channelID = getValueByTagName(context,AuthConstants.CHANNEL_ID);




            logger.debug("Retrieving the basic auth token details for sourceAppID:{}, channelID:{}",sourceAppID,channelID );

            /*
             * Retrieve the Basic Auth Token for the key
             * sourceAppID~channelID
             * e.g.
             * MOBILEAPP~ONLINE
             * or
             * MYWEBAPP~PORTAL
             */
            String encodedBasicAuthCredentials = getAuthorizationDetails (sourceAppID, channelID);
            String[] userDetailsPair = getUserDetailsPairFromBasicAuthCredentials(encodedBasicAuthCredentials);
            logger.debug("Obtained the userDetailsPair:{}",userDetailsPair);
            if(userDetailsPair !=null && userDetailsPair.length ==2) {
                logger.debug("Settings the context header:{}",userDetailsPair);
                logger.debug("Settings the context with username:{} and password:{}",userDetailsPair[0],userDetailsPair[1]);
                context.put("javax.xml.ws.security.auth.username", userDetailsPair[0]);
                context.put("javax.xml.ws.security.auth.password", userDetailsPair[1]);

                //Came across a forum where it was recommended to call saveChanges() for container starting from Tomcat v8.0
                context.getMessage().saveChanges();
            }
            else {
                logger.error("The authorization header is not set because of unavailability for sourceAppID:{}, channelID:{}",sourceAppID,channelID );
            }

        } else {

            isOutBound = false;
        }

    }catch (Exception e) {
        logger.error("Exception  in handleMessage:{}", e.getMessage());
        if(logger.isDebugEnabled()){
            logger.error("Exception  in handleMessage:" , e);
        }
    }

    logger.debug("handleMessage()-end");
    return isOutBound;
}

The same code works fine via Junit

But when I test via JBOSS EAP 7.0, observed that the Authorization header is not set. Also observed that inside JBOSS, the CXF takes precedence as the client impl and its not setting the Authorization header

Any pointers will be really helpful


Solution

  • This issue was resolved by using org.apache.cxf.jaxws.JaxWsProxyFactoryBean and replaced the handler with an outInterceptor

    So now the configuration looks like

    <bean id="saajOutInterceptor" class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
    <bean id="myServices" factory-bean="myServicesClientFactory"
            factory-method="create" />
    <bean id="myServicesClientFactory"
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceInterface"
            value="training.service.mybusinessservices.myBusinessServicesPort" />
        <property name="wsdlLocation" value="${my.service.wsdlDocumentUrl}" />
        <property name="address" value="${my.service.endpoint}" />
            <property name="outInterceptors">
                <list>
                    <ref bean="saajOutInterceptor" /> <!-- This important, if we need to read any data from the requestXml and generate dynamic auth headers -->
                    <ref bean="clientAuthInterceptor" />
                </list>
            </property>
    </bean>
    

    Reference articles

    How to dynamically add HTTP headers in CXF client?

    Note Ensure that cxf-rt-bindings-soap-3.x.x.jar is included (The SAAJOutInterceptor interceptor)

    So with the following approach, now the Basic Auth header is generated dynamically and working