无法通过容器内的 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 并正常工作
我们需要为所有出站 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 并正常工作