我有一个需要 SenderVouches 身份验证的服务,它适用于基于 CXF 的 Java 应用程序。但现在我们需要在其 SOAP/WS-Security 堆栈中集成一个不支持 SenderVouches 的基于 .NET 的应用程序。
我环顾四周,发现这篇文章解释了 SenderVouches 背后的 WS-Trust 的基本理论:https://www.xml.com/pub/a/ws/2003/06/24/ws-trust .html
在那里我找到了这张图,它可以使用 SOAP“网关”来解决我们面临的问题。 Apache CXF 是否支持实现网关部分?
我四处搜索,但大多数结果与实现 SOAP 网关无关。
结合使用 Membrane API Gateway 和 CXF 可能会有所帮助:
package com.example.wsproxy.membrane;
import com.example.wsproxy.configmodel.ServiceProperties;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.http.Response;
import com.predic8.membrane.core.interceptor.AbstractInterceptor;
import com.predic8.membrane.core.interceptor.Outcome;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.cxf.jaxws.DispatchImpl;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
@Slf4j
public class MembraneCxfInterceptor extends AbstractInterceptor {
protected final Dispatch<StreamSource> dispatch;
protected final MessageFactory messageFactory;
public MembraneCxfInterceptor(ServiceProperties serviceProperties) throws SOAPException {
dispatch = setupDispatch(serviceProperties);
messageFactory = MessageFactory.newInstance();
}
private Dispatch<StreamSource> setupDispatch(ServiceProperties serviceProperties){
log.debug("Setting up dispatcher for {}", serviceProperties.getServiceName());
Service service = Service.create(serviceProperties.getWsdlLocation(), serviceProperties.getServiceName());
Dispatch<StreamSource> d = service.createDispatch(service.getPorts().next(), StreamSource.class, Service.Mode.MESSAGE);
log.debug("Warming up CXF...");
// warming up CXF
((DispatchImpl<StreamSource>) d).getClient().getConduitSelector().getEndpoint();
return d;
}
@Override
public Outcome handleRequest(Exchange exc) throws Exception {
StreamSource request = new StreamSource(exc.getRequest().getBodyAsStream());
StreamSource response = dispatch.invoke(request);
@SuppressWarnings("squid:S2095") // closed by membrane
PipedInputStream pis = new PipedInputStream();
@SuppressWarnings("squid:S2095") // closed in finally block
PipedOutputStream pos = new PipedOutputStream(pis);
new Thread(() -> {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
// TODO: Faults?
t.transform(response, new StreamResult(pos));
} catch (TransformerException e) {
throw new CxfInterceptorTransformerException(e);
} finally {
try {
pos.close();
} catch (IOException e) {
log.error("error closing piped output stream!", e);
}
}
}).start();
exc.setResponse(Response.ok().body(pis, true).build());
return Outcome.RETURN;
}
static class CxfInterceptorTransformerException extends RuntimeException {
public CxfInterceptorTransformerException(TransformerException e){
super(e);
}
}
}
package com.example.wsproxy.configmodel;
import lombok.Data;
import javax.xml.namespace.QName;
import java.net.URL;
@Data
public class ServiceProperties {
private URL wsdlLocation;
private QName serviceName;
}