Reputation: 13002
I'm trying to build an endpoint that will receive SOAP messages from a client. The message I'm receiving contains a username and password inside the soap header ...
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://www.company.com/Application">
<soapenv:Header xmlns:wsse="http://__________.xsd">
<wsse:Security >
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<wsse:Password>password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
I'm using Spring WS - the obvious solution is to create a filter inside web.xml
that will bypass Spring WS completely, parse the SOAP message, extract the username and password and then continue to Spring WS which will parse the SOAP again.
Is there a way to get the content of the header without circumventing Spring WS?
I've tried adding a bean inside sws:interceptors
:
<sws:interceptors>
<!-- extract Security details from Header -->
<bean class="com.company.application.service.SecurityInterceptorService" />
<!-- log full Body of request -->
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
<!-- validate Request against XSD to make sure it's a valid request -->
<bean id="CompanyApplication" class="com.company.application.interceptor.ValidatingInterceptor">
<property name="schema" value="/WEB-INF/_______________.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="true" />
</bean>
</sws:interceptors>
and then implementing that class:
public class SecurityInterceptorService implements EndpointInterceptor {
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleRequest") ;
System.out.println("---------------");
return true;
}
@Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleResponse");
System.out.println("---------------");
return true;
}
@Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("---------------");
System.out.println("handleFault");
System.out.println("---------------");
return true;
}
@Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
System.out.println("---------------");
System.out.println("afterCompletion");
System.out.println("---------------");
}
}
endpoint
only contains data about the endpoint inside handleRequest
and after traversing through many layers and layers inside messageContext
while in debug mode, I can't seem to spot the content of the header.
Is the content I'm looking for inside messageContext
and if so, how do I access it?
Upvotes: 3
Views: 23486
Reputation: 712
There is no easy way to unmarshall Soap headers with Spring-ws (it's currently not supported) However, you can access the SoapHeaderElement in your @PayloadRoot annotated method, and do the process of unmarshalling with JAXB.
@Endpoint
public class SubmitEndpoint implements EndpointInterface {
private static final String NAMESPACE_URI = "http://www.example.com/namespace";
private Security unmarshallSecurityFromSoapHeader(SoapHeaderElement header) {
Security security = null;
try {
JAXBContext context = JAXBContext.newInstance(Security.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
security = (Security) unmarshaller.unmarshal(header.getSource());
} catch (JAXBException e) {
e.printStackTrace();
}
return security;
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "submit")
@ResponsePayload
public SubmitResponse submit(@RequestPayload Submit submit, @SoapHeader(
value = "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security") SoapHeaderElement wsseSecurityHeader) throws JAXBException {
Security security = unmarshallSecurityFromSoapHeader(wsseSecurityHeader);
}
}
Security.java
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = Security.SECURITY_NS, name = "Security")
public class Security {
public static final String SECURITY_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
@XmlElement(namespace = SECURITY_NS, name = "UsernameToken")
private UsernameToken usernameToken;
}
UsernameToken.java
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = Security.SECURITY_NS, name = "UsernameToken")
public class UsernameToken {
@XmlElement(name = "Username", namespace = Security.SECURITY_NS)
private String username;
@XmlElement(name = "Password", namespace = Security.SECURITY_NS)
private String password;
}
Upvotes: 5
Reputation: 1034
If you are using spring-boot you can use this kind of configuration:
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchema(resourceSchema());
interceptors.add(validatingInterceptor);
}
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/api/*");
}
@Bean(name = "registros")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("ResourcePort");
wsdl11Definition.setLocationUri("/api");
wsdl11Definition.setTargetNamespace("http://resource.com/schema");
wsdl11Definition.setSchema(resourceSchema());
return wsdl11Definition;
}
@Bean
public XsdSchema resourceSchema() {
return new SimpleXsdSchema(new ClassPathResource("registro.xsd"));
}
}
In this example the addInterceptors method is the important one, the others 3 are basic to expose a WSDL API.
Maybe it'll be useful for someone else.
Upvotes: 3
Reputation: 3091
From the messageContext object, you can retrieve either the request or the response (In your case, I guess you need the request).
The request/response is basically a WebServiceMessage. If you examine the webServiceMessage, you will see that the object can be casted to a SoapMessage. From the soap message, you can now get the soap header.
WebServiceMessage webServiceMessageRequest = messageContext_.getRequest();
SoapMessage soapMessage = (SoapMessage) webServiceMessageRequest;
SoapHeader soapHeader = soapMessage.getSoapHeader()
Afterwards, You might want to get the source object and convert it to a DOMSource object and then get the Node object which make the information retrieval much easier.
Source bodySource = soapHeader .getSource();
DOMSource bodyDomSource = (DOMSource) bodySource;
Node bodyNode = _bodyDomSource.getNode();
Upvotes: 12