我在使用Apache CXF为Web服务请求设置HTTP Authorization标头时遇到了一些问题。我通过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" />
标头是通过MyHTTPAuthInterceptor设置的,如下所示:
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()))));
}
使用用户名并将两者都设置为'test',日志中的所有内容似乎都正常:
Headers: {SOAPAction=[""], Accept=[*/*], Authorization=[Basic dGVzdDp0ZXN0]}
但是,服务器返回HTTP 401:Unauthorized。
不知道出了什么问题,我通过更改我的Web服务客户端工厂代码采取了另一种方法。我在客户端的管道中添加了一个基本授权策略,如下所示:
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy();
authorizationPolicy.setUserName("test");
authorizationPolicy.setPassword("test");
authorizationPolicy.setAuthorizationType("Basic");
httpConduit.setAuthorization(authorizationPolicy);
再次测试我的设置,相同的日志(尽管顺序不同):
Headers: {SOAPAction=[""], Authorization=[Basic dGVzdDp0ZXN0], Accept=[*/*]}
现在服务器的响应是200 OK!
你可能会想到问题解决了,但第二种方法并不适用于我。我的应用程序是一个多租户环境,所有用户名和密码都不同。使用第二种方法,我无法重用我的客户端。
如何让我的拦截器正常工作?我是否陷入了错误的阶段?标题的顺序是否重要?如果是这样,我该如何更改?
答案 0 :(得分:6)
我的设置几乎和你的设置完全相同,但我将拦截器放在PRE_PROTOCOL阶段。到目前为止,我没有遇到任何问题。你可以尝试一下。
我认为POST_PROTOCOL太晚了,因为已经有太多的内容写入了流。
答案 1 :(得分:3)
如果您希望外部化客户端并且身份验证最佳方法是在spring上下文中设置httpConduit ..
**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)在name属性中提及SOAP地址。您可以在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" />
,然后强>
...
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>
或 b)name属性应为{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>