diff --git a/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java index a6b6a917..d19a59ac 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java @@ -21,8 +21,14 @@ import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.util.Assert; +import org.springframework.ws.client.WebServiceClientException; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.client.support.interceptor.ClientInterceptor; import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.EndpointInterceptor; +import org.springframework.ws.server.endpoint.mapping.AbstractEndpointMapping; import org.springframework.ws.soap.SoapBody; import org.springframework.ws.soap.SoapFault; import org.springframework.ws.soap.SoapHeaderElement; @@ -31,46 +37,69 @@ import org.springframework.ws.soap.server.SoapEndpointInterceptor; import org.springframework.ws.soap.soap11.Soap11Body; /** - * Interceptor base class for interceptors that handle WS-Security. + * Interceptor base class for interceptors that handle WS-Security. Can be used on the server side, registered in a + * {@link AbstractEndpointMapping#setInterceptors(EndpointInterceptor[]) endpoint mapping}; or on the client side, on + * the {@link WebServiceTemplate#setInterceptors(ClientInterceptor[]) web service template}. *
- * Subclasses of this base class can be configured to validate incoming and secure outgoing messages. By default, both - * are on. + * Subclasses of this base class can be configured to secure incoming and secure outgoing messages. By default, both are + * on. * * @author Arjen Poutsma * @since 1.0.0 */ -public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInterceptor { +public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInterceptor, ClientInterceptor { + + /** Logger available to subclasses. */ + protected final Log logger = LogFactory.getLog(getClass()); private static final QName WS_SECURITY_NAME = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security"); - /** - * Logger available to subclasses. - */ - protected final Log logger = LogFactory.getLog(getClass()); - private boolean secureResponse = true; private boolean validateRequest = true; - /** - * Indicates whether outgoing responsed are to be secured. Defaults totrue.
- */
- public void setSecureResponse(boolean secureResponse) {
- this.secureResponse = secureResponse;
- }
+ private boolean secureRequest = true;
- /**
- * Indicates whether incoming request are to be validated. Defaults to true.
- */
+ private boolean validateResponse = true;
+
+ /** Indicates whether server-side incoming request are to be validated. Defaults to true. */
public void setValidateRequest(boolean validateRequest) {
this.validateRequest = validateRequest;
}
+ /** Indicates whether server-side outgoing responses are to be secured. Defaults to true. */
+ public void setSecureResponse(boolean secureResponse) {
+ this.secureResponse = secureResponse;
+ }
+
+ /** Indicates whether client-side outgoing requests are to be secured. Defaults to true. */
+ public void setSecureRequest(boolean secureRequest) {
+ this.secureRequest = secureRequest;
+ }
+
+ /** Indicates whether client-side incoming responses are to be validated. Defaults to true. */
+ public void setValidateResponse(boolean validateResponse) {
+ this.validateResponse = validateResponse;
+ }
+
+ /*
+ * Server-side
+ */
+
+ /**
+ * Validates a server-side incoming request. Delegates to {@link #validateMessage(SoapMessage)} if the {@link
+ * #setValidateRequest(boolean) validateRequest} property is true.
+ *
+ * @param messageContext the message context, containing the request to be validated
+ * @param endpoint chosen endpoint to invoke
+ * @return true if the request was valid; false otherwise.
+ * @throws Exception in case of errors
+ * @see #validateMessage(SoapMessage)
+ */
public final boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
if (validateRequest) {
- Assert.isTrue(messageContext.getRequest() instanceof SoapMessage,
- "WsSecurityInterceptor requires a SoapMessage request");
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
try {
validateMessage((SoapMessage) messageContext.getRequest());
return true;
@@ -87,10 +116,20 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
}
}
+ /**
+ * Secures a server-side outgoing response. Delegates to {@link #secureMessage(SoapMessage)} if the {@link
+ * #setSecureResponse(boolean) secureResponse} property is true.
+ *
+ * @param messageContext the message context, containing the response to be secured
+ * @param endpoint chosen endpoint to invoke
+ * @return true if the response was secured; false otherwise.
+ * @throws Exception in case of errors
+ * @see #secureMessage(SoapMessage)
+ */
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
if (secureResponse) {
- Assert.isTrue(messageContext.getResponse() instanceof SoapMessage,
- "WsSecurityInterceptor requires a SoapMessage response");
+ Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
try {
secureMessage((SoapMessage) messageContext.getResponse());
return true;
@@ -107,9 +146,7 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
}
}
- /**
- * Returns true, i.e. faults are not secured.
- */
+ /** Returns true, i.e. fault responses are not secured. */
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
return true;
}
@@ -118,6 +155,72 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
return WS_SECURITY_NAME.equals(headerElement.getName());
}
+ /*
+ * Client-side
+ */
+
+ /**
+ * Secures a client-side outgoing request. Delegates to {@link #secureMessage(SoapMessage)} if the {@link
+ * #setSecureRequest(boolean) secureRequest} property is true.
+ *
+ * @param messageContext the message context, containing the request to be secured
+ * @return true if the response was secured; false otherwise.
+ * @throws Exception in case of errors
+ * @see #secureMessage(SoapMessage)
+ */
+ public final boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
+ if (secureRequest) {
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
+ try {
+ secureMessage((SoapMessage) messageContext.getRequest());
+ return true;
+ }
+ catch (WsSecuritySecurementException ex) {
+ return handleSecurementException(ex, messageContext);
+ }
+ catch (WsSecurityFaultException ex) {
+ return handleFaultException(ex, messageContext);
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Validates a client-side incoming response. Delegates to {@link #validateMessage(SoapMessage)} if the {@link
+ * #setValidateResponse(boolean) validateResponse} property is true.
+ *
+ * @param messageContext the message context, containing the response to be validated
+ * @return true if the request was valid; false otherwise.
+ * @throws Exception in case of errors
+ * @see #validateMessage(SoapMessage)
+ */
+ public final boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
+ if (validateResponse) {
+ Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
+ Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
+ try {
+ validateMessage((SoapMessage) messageContext.getResponse());
+ return true;
+ }
+ catch (WsSecurityValidationException ex) {
+ return handleValidationException(ex, messageContext);
+ }
+ catch (WsSecurityFaultException ex) {
+ return handleFaultException(ex, messageContext);
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ /** Returns true, i.e. fault responses are not validated. */
+ public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
+ return true;
+ }
+
/**
* Handles an securement exception. Default implementation logs the given exception, and returns
* false.
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
index 016c6428..c9245b2f 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
@@ -20,6 +20,7 @@ import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import junit.framework.TestCase;
+
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapMessage;
@@ -35,7 +36,7 @@ public class XwsSecurityInterceptorTest extends TestCase {
messageFactory = MessageFactory.newInstance();
}
- public void testhandleRequest() throws Exception {
+ public void testhandleServerRequest() throws Exception {
final SOAPMessage request = messageFactory.createMessage();
final SOAPMessage validatedRequest = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
@@ -57,7 +58,7 @@ public class XwsSecurityInterceptorTest extends TestCase {
assertEquals("Invalid request", validatedRequest, ((SaajSoapMessage) context.getRequest()).getSaajMessage());
}
- public void testhandleResponse() throws Exception {
+ public void testhandleServerResponse() throws Exception {
final SOAPMessage securedResponse = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
@@ -74,8 +75,53 @@ public class XwsSecurityInterceptorTest extends TestCase {
SOAPMessage request = messageFactory.createMessage();
MessageContext context =
new DefaultMessageContext(new SaajSoapMessage(request), new SaajSoapMessageFactory(messageFactory));
+ context.getResponse();
interceptor.handleResponse(context, null);
assertEquals("Invalid response", securedResponse, ((SaajSoapMessage) context.getResponse()).getSaajMessage());
}
+ public void testhandleClientRequest() throws Exception {
+ final SOAPMessage request = messageFactory.createMessage();
+ final SOAPMessage securedRequest = messageFactory.createMessage();
+ XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
+
+ protected void secureMessage(SoapMessage soapMessage) throws XwsSecuritySecurementException {
+ SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
+ assertEquals("Invalid message", request, saajSoapMessage.getSaajMessage());
+ saajSoapMessage.setSaajMessage(securedRequest);
+ }
+
+ protected void validateMessage(SoapMessage message) throws WsSecurityValidationException {
+ fail("validate not expected");
+ }
+
+ };
+ MessageContext context =
+ new DefaultMessageContext(new SaajSoapMessage(request), new SaajSoapMessageFactory(messageFactory));
+ interceptor.handleRequest(context);
+ assertEquals("Invalid request", securedRequest, ((SaajSoapMessage) context.getRequest()).getSaajMessage());
+ }
+
+ public void testhandleClientResponse() throws Exception {
+ final SOAPMessage validatedResponse = messageFactory.createMessage();
+ XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
+
+ protected void secureMessage(SoapMessage message) throws XwsSecuritySecurementException {
+ fail("secure not expected");
+ }
+
+ protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidationException {
+ SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
+ saajSoapMessage.setSaajMessage(validatedResponse);
+ }
+
+ };
+ SOAPMessage request = messageFactory.createMessage();
+ MessageContext context =
+ new DefaultMessageContext(new SaajSoapMessage(request), new SaajSoapMessageFactory(messageFactory));
+ context.getResponse();
+ interceptor.handleResponse(context);
+ assertEquals("Invalid response", validatedResponse, ((SaajSoapMessage) context.getResponse()).getSaajMessage());
+ }
+
}
\ No newline at end of file