【问题标题】:HTTP basic authentication through CXF interceptor not working通过 CXF 拦截器的 HTTP 基本身份验证不起作用
【发布时间】:2012-08-12 15:13:55
【问题描述】:

我在使用 Apache CXF 为 Web 服务请求设置 HTTP 授权标头时遇到了一些问题。我在春季完成了我的客户设置:

<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()))));
}

使用用户名并且都设置为“测试”,日志中的一切似乎都正常:

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!

您可能会认为问题已解决,但第二种方法对我来说并不适用。我的应用程序是一个多租户环境,都有不同的用户名和密码。使用第二种方法,我无法重用我的客户端。

如何让我的拦截器正常工作?我是否插入了错误的阶段?标题的顺序重要吗?如果是这样,我该如何更改?

【问题讨论】:

    标签: java web-services cxf basic-authentication


    【解决方案1】:

    我的设置与您的设置几乎完全相同,但我将拦截器置于 PRE_PROTOCOL 阶段。到目前为止,我还没有遇到任何问题。你可以试试。

    我认为 POST_PROTOCOL 为时已晚,因为已经向流中写入了太多内容。

    【讨论】:

    • 好的,我必须在这里更正自己。犯了个错误。它确实有效!非常感谢您的回答,特此采纳:)
    【解决方案2】:

    如果您希望将客户端和身份验证外部化,最好的方法是在 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) 在名称属性中提及 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) 名称属性应该是 {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>
    

    【讨论】:

      猜你喜欢
      • 2017-11-16
      • 1970-01-01
      • 2014-01-22
      • 2014-12-31
      • 2011-03-02
      • 2014-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多