【问题标题】:JAX-WS Consuming web service with WS-Security and WS-AddressingJAX-WS 使用 WS-Security 和 WS-Addressing 的 Web 服务
【发布时间】:2010-10-16 12:03:23
【问题描述】:

我正在尝试使用 JAX-WS (Metro) 开发一个独立的 Java Web 服务客户端,该客户端使用 WS-Security 和用户名令牌身份验证(密码摘要、随机数和时间戳)和时间戳验证以及基于 SSL 的 WS-Addressing。

我必须使用的 WSDL 没有定义任何安全策略信息。当 WSDL 不包含此信息时,我无法准确地弄清楚如何添加此标头信息(正确的方法)。我发现使用 Metro 的大多数示例都围绕着使用 Netbeans 从 WSDL 自动生成,这对我毫无帮助。我研究了 WSIT、XWSS 等,但没有太多清晰或方向。 JBoss WS Metro 看起来也没什么好运气。

任何人都有这样做的经验或对如何完成这项任务有建议?即使将我指向正确的方向也会有所帮助。我不限于特定的技术,它必须是基于 Java 的。

【问题讨论】:

    标签: java web-services jax-ws ws-security java-metro-framework


    【解决方案1】:

    我确实最终解决了这个问题,但我转向了另一个方向。我的解决方案是使用 CXF 2.1 及其 JAX-WS 实现,将 CXF 的强大功能与我已有的现有 Spring 基础设施相结合。起初我持怀疑态度,因为 CXF 需要大量的 jar,但最终它提供了最好和最简单的解决方案。

    改编自 CXF website for client configuration 的示例,我在 spring 中使用自定义 CXF JAXWS 命名空间,并使用 Out Interceptor 进行用户名令牌身份验证(密码摘要、随机数和时间戳)和时间戳验证。完成这项工作的唯一其他步骤是创建我自己的密码回调处理程序,该处理程序为每个出站 SOAP 请求执行。

    对于 SSL 配置,我再次求助于 CXF and its SSL support via conduits,虽然我无法让 SSL 使用特定的 http:conduit 名称,但我不得不使用不推荐用于生产环境的通用名称。

    以下是我的配置文件的示例。

    Spring 配置文件

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xmlns:sec="http://cxf.apache.org/configuration/security"
        xmlns:http="http://cxf.apache.org/transports/http/configuration"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:cxf="http://cxf.apache.org/core"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
        http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
        http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
    
        <context:property-placeholder location="meta/my.properties" />
        <context:component-scan base-package="com.foo" />
    
        <import resource="remoting.xml" />
        <jaxws:client id="myWebService" address="${my.endpointAddress}"
                      serviceClass="com.foo.my.ServicePortType">
    
    <!-- Testing only, adds logging of entire message in and out -->
    <jaxws:outInterceptors>
        <ref bean="TimestampUsernameToken_Request" />
        <ref bean="logOutbound" />
    </jaxws:outInterceptors>
    <jaxws:inInterceptors>
            <ref bean="logInbound" />
        </jaxws:inInterceptors>
        <jaxws:inFaultInterceptors>
            <ref bean="logOutbound" />
        </jaxws:inFaultInterceptors>
    
    <!-- Production settings -->
    <!--
        <jaxws:outInterceptors> <ref bean="TimestampUsernameToken_Request" />
        </jaxws:outInterceptors>
        -->
    </jaxws:client >
    
    
    
    <!--
        CXF Interceptors for Inbound and Outbound messages
        Used for logging and adding Username token / Timestamp Security Header to SOAP message
    -->
    <bean id="logInbound" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
    <bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
    
    <bean id="TimestampUsernameToken_Request" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
        <constructor-arg>
            <map>
                <entry key="action" value="UsernameToken Timestamp" />
                <entry key="user" value="${my.group}.${my.userId}" />
                <entry key="passwordType" value="PasswordDigest" />
                <entry key="passwordCallbackClass" value="com.foo.my.ClientPasswordHandler" />
            </map>
        </constructor-arg>
    </bean>
    
    <!--
        http:conduit namespace is used to configure SSL using keystores, etc
        *.http-conduit works but CXF says its only supposed to be for temporary use (not production),
        well until the correct way works, we're going to use it.
    -->
    <http:conduit name="*.http-conduit">
        <http:tlsClientParameters   
                      secureSocketProtocol="SSL">
                      <!--
              <sec:trustManagers>
            <sec:keyStore type="JKS"
                             password="${my.truststore.password}"
                             file="${my.truststore.file}" />
                      </sec:trustManagers>
                      -->
                      <sec:keyManagers keyPassword="${my.keystore.password}">
                        <sec:keyStore type="JKS"
                             password="${my.keystore.password}"
                             file="${my.keystore.file}" />
                      </sec:keyManagers>
    
                      <!-- Cipher suites filters specify the cipher suite to allow/disallow in SSL communcation  -->
                      <sec:cipherSuitesFilter>
                        <sec:include>.*_WITH_3DES_.*</sec:include>
                        <sec:include>.*_EXPORT_.*</sec:include>
                        <sec:include>.*_EXPORT1024_.*</sec:include
                        <sec:include>.*_WITH_DES_.*</sec:include
                        <sec:exclude>.*_WITH_NULL_.*</sec:exclude
                        <sec:exclude>.*_DH_anon_.*</sec:exclude>
                      </sec:cipherSuitesFilter>
        </http:tlsClientParameters>
    </http:conduit>
    </beans>
    

    Java 客户端密码处理程序:

    import java.io.IOException;
    
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.UnsupportedCallbackException;
    
    import org.apache.log4j.Logger;
    import org.apache.ws.security.WSPasswordCallback;
    
    
    /**
     * <p>
     * Provides a callback handler for use processing outbound/inbound SOAP messages.
     * ClientPasswordHandler sets the password used in the WS-Security UsernameToken 
     * SOAP header.
     * 
     * </p>
     * 
     * Created: Apr 1, 2009
     * @author Jared Knipp
     * 
     */
    public final class ClientPasswordHandler implements CallbackHandler {
        protected static Logger log = Logger.getLogger(ClientPasswordHandler.class);
    
        private static final PropertyManager PROPS = PropertyManager.getInstance();
        private static String PASSWORD = PROPS.getPassword();
        private static boolean IS_PASSWORD_CLEAR = PROPS.getIsClearPassword();
    
        /**
         * Client password handler call back.  This method is used to provide
         * additional outbound (or could be inbound also) message processing.
         * 
         * Here the method sets the password used in the UsernameToken SOAP security header
         * element in the SOAP header of the outbound message.  For our purposes the clear 
         * text password is SHA1 hashed first before it is hashed again along with the nonce and 
         * current timestamp in the security header.
         */
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            if(log.isDebugEnabled()) { log.debug("Setting password for UsernameToken"); }
            WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
    
    
            // Check to see if the password is already Hashed via SHA1, if not then hash it first
            if(IS_PASSWORD_CLEAR) {
                synchronized(this) {
                    PASSWORD = PasswordDigestUtil.doPasswordDigest(PASSWORD);
                    IS_PASSWORD_CLEAR = false;
                    PROPS.setIsClearPassword(IS_PASSWORD_CLEAR);
                    PROPS.setPassword(PASSWORD);
                    PROPS.saveProperties();
                }
            }
    
            pc.setPassword(PASSWORD);
        }
    }
    

    【讨论】:

      【解决方案2】:

      如果信息不在 WSDL 中,您确定它在 WSDL 描述的服务中吗? WSDL 旨在提供描述服务所需的所有信息,包括使用服务所需的安全策略。

      WSDL 来自什么平台? WSDL 是否有可能不是完整的描述?例如,它可能是一个 WSDL,它包含在另一个 确实提供安全信息的 WSDL 中。

      【讨论】:

      • 让我澄清一下,我有权访问的不是 WSDL。我的文档有漏洞(没有提到时间戳),所以排除 WSDL 信息不会让我感到惊讶。
      • 嗯,您将需要真正的 WSDL 来调用真正的服务。这就是它的全部内容。
      • 那么 WSDL 不可能忽略它,但它是在服务器上实现的?服务器是使用某些版本的 Axis 的 Java(可能是 JBoss 或 Websphere)。
      • WSDL 没有可能正确忽略这一点。我看到过早期版本的 Axis 假设客户端和服务都是 Axis,所以不要在 WSDL 中做任何事情。但不是安全性。
      【解决方案3】:

      这里有一篇文章解释了如何在 CXF 中使用 WS-Security 配置客户端和服务器:JAX-WS Web Services with Spring and CXF

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 2012-04-12
        • 2011-04-26
        相关资源
        最近更新 更多