我们需要将“基本身份验证授权”标头设置为所有出站SOAP调用。我们的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标头(与使用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进行测试时,观察到未设置Authorization标头。 还观察到在JBOSS内部,CXF优先作为客户端impl,并且未设置Authorization标头
任何指针都会很有帮助
答案 0 :(得分:0)
使用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?
注意确保已包含cxf-rt-bindings-soap-3.x.x.jar( SAAJOutInterceptor拦截器)
因此,通过以下方法,现在基本身份验证标头是动态生成的并且可以正常工作