无法通过容器内的 HandlerResolver 动态设置 Basic Auth header

Unable to set Basic Auth header dynamically via HandlerResolver inside container

我们需要为所有出站 SOAP 调用设置基本身份验证授权 header。我们的 J2EE 应用程序使用 spring 作为 SOAP 客户端。

<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>

我们的要求是根据我们调用的 serviceName 从中央存储中获取用户名和密码。因此,方法是使用 handlerResolver 通过拦截器设置 http header(相对于使用 JaxWsProxy....用户名和密码属性)

我们的 handlerResolver 拦截器实现

@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;
}

相同的代码通过 Junit 工作正常

但是当我通过 JBOSS EAP 7.0 进行测试时,发现未设置授权 header。 还观察到在 JBOSS 内部,CXF 优先作为客户端实现,它没有设置授权 header

任何指点都会很有帮助

此问题已通过使用 org.apache.cxf.jaxws.JaxWsProxyFactoryBean 解决,并将处理程序替换为 outInterceptor

所以现在配置看起来像

<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>

参考文章

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)

因此,通过以下方法,现在可以动态生成基本身份验证 header 并正常工作