CXF:如何从CXF客户端重新发送原始请求?

时间:2017-02-22 11:56:00

标签: java soap cxf wss4j

我是SOA开发环境中的CXF用户。

我想知道我的问题是否有CXF解决方案。这是我的需要。 我们开发了一个服务于JAXWS端点的webapp,端点实现包括通过拦截器分析请求,将来自请求的数据从Java服务层存储到数据库,并通过CXF客户端将原始请求重新发送到另一个服务器。 关键是我们的一些请求包含DSIG签名(https://www.w3.org/TR/xmldsig-core/)或签名的SAML断言。 我们需要重新发送请求而不从CXFClient更改它们(例如代理)。 CXF用于将编组的对象发送到服务器,但这样就不会发送原始流

有没有办法从Java CXFClient重新发送来自服务层的传入请求而不改变它(签名取决于请求的格式:空格,名称空间前缀,回车...)?我们更喜欢CXFClient,因为我们希望重用我们自制的CXF拦截器来记录传出的请求。

我们已经测试了一个拦截器,打算在将outputStream发送到服务器之前用原始请求替换outputStream,我们使用了这个答案:How To Modify The Raw XML message of an Outbound CXF Request?,但是我们仍然是KO,CXF仍然发送由编组对象。请参阅下面的代码。

上下文: - CXF 2.7.18(JDK 6)和3.1.10(JDK 8) - 平台:windows 7 64bit / rhel 7 64bit - Apache Tomcat 7 - 用于分析传入流量的Tcpdump

我们客户的代码示例:

    final Client cxfClient = org.apache.cxf.frontend.ClientProxy.getClient( portType );
    cxfClient.getInInterceptors().clear();
cxfClient.getOutInterceptors().clear();
cxfClient.getOutFaultInterceptors().clear();
cxfClient.getRequestContext().put(CustomStreamerInterceptor.STREAM_TO_SEND,
PhaseInterceptorChain.getCurrentMessage().getContent( InputStream.class ) );
cxfClient.getOutInterceptors().add( new CustomStreamerInterceptor() );
org.apache.cxf.transport.http.HTTPConduit http = (org.apache.cxf.transport.http.HTTPConduit) cxfClient.getConduit();
...
port.doSomething(someRequest);

CustomStreamerInterceptor:

package test;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.SoapOutEndingInterceptor;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CacheAndWriteOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.Phase;
public class CustomStreamerInterceptor extends AbstractOutDatabindingInterceptor {
       public static final String STREAM_TO_SEND = "STREAM_TO_SEND";
       public CustomStreamerInterceptor () {
             super( Phase.WRITE_ENDING );
             addAfter( SoapOutEndingInterceptor.class.getName() );
       }
       @Override
       public void handleMessage( Message message ) throws Fault {
             try {
                    InputStream toSend = (InputStream) message.get( STREAM_TO_SEND );
                    if ( toSend != null ) {
                           toSend.reset();
                           LoadingByteArrayOutputStream lBos = new LoadingByteArrayOutputStream();
                           IOUtils.copy( toSend, lBos );
                           CacheAndWriteOutputStream cawos = (CacheAndWriteOutputStream) message.getContent( OutputStream.class );
                           cawos.resetOut( lBos, false );//fail !
                    }
             }
             catch ( Exception e ) {
                    throw new Fault( e );
             }
       }
}

感谢您的帮助,这将非常有用。

1 个答案:

答案 0 :(得分:0)

我认为创建一个经典的" HTTP客户端因为CXF不是为这种情况而设计的,所以使用它来编组从java到XML的对象更常见...... 幸运的是,我用拦截器处理这个问题。您可以编写一个拦截器,用于复制CXF准备发送到服务器的输出流对象中的流。您需要注意拦截器的阶段和顺序,因为如果使用Logging拦截器,则可能需要记录传出流。这个拦截器可以完成这项工作,确保它在任何日志记录拦截器之后运行。代码为CXF 2.7.18:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang.CharEncoding;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.StaxOutInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PassePlatClientInterceptorOut extends AbstractPhaseInterceptor<Message> {
private static final Logger LOG = LoggerFactory.getLogger( PassePlatClientInterceptorOut.class );

private final Exchange exchangeToReadFrom;

public PassePlatClientInterceptorOut( final Exchange exchange ) {
    super( Phase.PRE_STREAM );
    addBefore( StaxOutInterceptor.class.getName() );
    this.exchangeToReadFrom = exchange;
}

@Override
public void handleMessage( Message message ) {
    InputStream is = (InputStream) exchangeToReadFrom.get( PassePlatServerInterceptorIn.PASSE_PLAT_INTERCEPTOR_STREAM_SERVEUR );
    if ( is != null ) {
        message.put( org.apache.cxf.message.Message.ENCODING, CharEncoding.UTF_8 );
        OutputStream os = message.getContent( OutputStream.class );
        try {
            IOUtils.copy( is, os );
            is.close();
        }
        catch ( IOException e ) {
            LOG.error( "Error ...", e );
            message.setContent( Exception.class, e );
            throw new Fault( new Exception( "Error ...", e ) );
        }
        boolean everythingOK = message.getInterceptorChain().doInterceptStartingAt( message,
                org.apache.cxf.interceptor.MessageSenderInterceptor.MessageSenderEndingInterceptor.class.getName() );
        if ( !everythingOK ) {
            LOG.error( "Error ?" );
            throw new Fault( new Exception( "Error ..." ) );
        }
    }
}

}

创建拦截器:

cxfClient.getInInterceptors().add( new PassePlatClientInterceptorIn( exchange ) );