How to add chain of certificate in spring ws client request - java

I have simply spring ws client which sending request to some url:
#SuppressWarnings("unchecked")
private JAXBElement<O> sendSyncSoapRequest(final JAXBElement<I> req, final String iszrUrl) {
if (iszrUrl != null) {
return (JAXBElement<O>) this.wsTemplate.marshalSendAndReceive(iszrUrl, req);
} else {
return (JAXBElement<O>) this.wsTemplate.marshalSendAndReceive(req);
}
}
Now I need attach chain of certificate to the soap request. How should I do this ? Please help

I'm not aware of any syntactic sugar in Spring for using certificate authentication on the client. However there may now be something I have missed. In the absence of someone else pointing out that there's a simple annotation that you can apply to your web service template, here's my thinking.
This isn't a fully step-by-step answer, but it should get you part way there. By using a WebServiceMessageCallback, you can modify the headers in your SOAP message before the message is sent. The code below demonstrates doing that to add a username and password to the headers.
You should be able to use the same mechanism to add certificates to the security headers in a similar manner. Take a look at the following document, which explains SOAP certificate-based authentication and shows example security headers for this on page 9.
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf
Object response = getWebServiceTemplate().marshalSendAndReceive(
exposureRequests,
new WebServiceMessageCallback() {
/**
* The doWithMessage callback enables us to modify the message after it has
* been built using the nice Spring/JAXB marshalling, just before it gets
* sent out.
*/
#Override
public void doWithMessage(WebServiceMessage message)
throws IOException, TransformerException {
applySecurityHeaders(message, SOAP_ACTION);
}
}
);
/**
* Add security headers to the outgoing message, so that the client is
* authenticated against the web service.
*/
private void applySecurityHeaders(WebServiceMessage message, String soapAction)
throws IOException, TransformerException {
Assert.isInstanceOf(SoapMessage.class, message);
SoapMessage soapMessage = (SoapMessage) message;
soapMessage.setSoapAction(soapAction);
SoapHeader header = soapMessage.getSoapHeader();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(getSecurityHeaderSource(), header.getResult());
soapMessage.writeTo(new LoggingOutputStream(log));
}
/**
* Returns the content required for a basic SOAP security header.
*/
private StringSource getSecurityHeaderSource() {
return new StringSource(
"<Security xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n "
+ "<UsernameToken>\n"
+ "<Username><![CDATA[" + username + "]]></Username>\n "
+ "<Password><![CDATA[" + password + "]]></Password>\n "
+ "</UsernameToken>\n"
+ "</Security>\n");
}

So I already solve this problem. I need to create WebServiceMessageSender with new httpClient which contains sslFactory with my certificates:
WebServiceMessageSender sender = new HttpComponentsMessageSender(HttpClients.custom()
.addInterceptorFirst(new RemoveSoapHeadersInterceptor()).setSSLSocketFactory(factory));
wsTemplate.setMessageSender(sender);
// copy & paste from HttpComponentsMessageSender:
/**
* HttpClient {#link org.apache.http.HttpRequestInterceptor} implementation that removes {#code Content-Length} and
* {#code Transfer-Encoding} headers from the request. Necessary, because SAAJ and other SOAP implementations set
* these headers themselves, and HttpClient throws an exception if they have been set.
*/
public static class RemoveSoapHeadersInterceptor implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
if (request instanceof HttpEntityEnclosingRequest) {
if (request.containsHeader(HTTP.TRANSFER_ENCODING)) {
request.removeHeaders(HTTP.TRANSFER_ENCODING);
}
if (request.containsHeader(HTTP.CONTENT_LEN)) {
request.removeHeaders(HTTP.CONTENT_LEN);
}
}
}
}

Related

Java Opensaml 3.4.6 : authnrequest subject is null - impossible to get user name

Developing a Java EE/JSF application, I am trying to include SAML sso functionality into it. Due to technical requirements (SAP BOBJ SDK) I need to use java 8, so I must stick with opensaml 3.x branch. As the application is some years old, I cannot add spring/spring-security to it just for SAML, that's why my code focuses on raw opensaml usage.
Mimicking the example code of this repository, I implemented the authentication basics:
This first code is called when I reach the "login" page. And send the AuthnRequest to my IDP
#Log4j2
#Named
public class SAMLAuthForWPBean implements Serializable {
private static final BasicParserPool PARSER_POOL = new BasicParserPool();
static {
PARSER_POOL.setMaxPoolSize(100);
PARSER_POOL.setCoalescing(true);
PARSER_POOL.setIgnoreComments(true);
PARSER_POOL.setIgnoreElementContentWhitespace(true);
PARSER_POOL.setNamespaceAware(true);
PARSER_POOL.setExpandEntityReferences(false);
PARSER_POOL.setXincludeAware(false);
final Map<String, Boolean> features = new HashMap<>();
features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE);
PARSER_POOL.setBuilderFeatures(features);
PARSER_POOL.setBuilderAttributes(new HashMap<>());
}
private String idpEndpoint = "url de azure por";
private String entityId = "glados";
private boolean isLogged;
#Inject
private LoginBean loginBean;
#Inject
private MainBean mainBean;
#Inject
private TechnicalConfigurationBean technicalConfigurationBean;
#PostConstruct
public void init() {
if (!PARSER_POOL.isInitialized()) {
try {
PARSER_POOL.initialize();
} catch (ComponentInitializationException e) {
LOGGER.error("Could not initialize parser pool", e);
}
}
XMLObjectProviderRegistry registry = new XMLObjectProviderRegistry();
ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
registry.setParserPool(PARSER_POOL);
// forge auth endpoint
}
public boolean needLogon() {
return isLogged;
}
public void createRedirection(HttpServletRequest request, HttpServletResponse response)
throws MessageEncodingException,
ComponentInitializationException, ResolverException {
// see this link to build authnrequest with metadata https://blog.samlsecurity.com/2011/01/redirect-with-authnrequest-opensaml2.html
init();
AuthnRequest authnRequest;
authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
authnRequest.setIssueInstant(DateTime.now());
FilesystemMetadataResolver metadataResolver = new FilesystemMetadataResolver(new File("wp.metadata.xml"));
metadataResolver.setParserPool(PARSER_POOL);
metadataResolver.setRequireValidMetadata(true);
metadataResolver.setId(metadataResolver.getClass().getCanonicalName());
metadataResolver.initialize();
/*
* EntityDescriptor urlDescriptor = metadataResolver.resolveSingle( new CriteriaSet( new BindingCriterion(
* Arrays.asList("urn:oasis:names:tc:SAML:2.0:bindings:metadata"))));
*/
/*entityId = "https://192.168.50.102:8443/360.suite/loginSAML.xhtml";*/
entityId = "glados";
//idp endpoint, je pense => à obtenir des metadata
authnRequest.setDestination(idpEndpoint);
authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
// app endpoint
authnRequest.setAssertionConsumerServiceURL("https://192.168.1.14:8443/360.suite/loginSAML.xhtml");
authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
authnRequest.setIssuer(buildIssuer());
authnRequest.setNameIDPolicy(buildNameIdPolicy());
MessageContext context = new MessageContext();
context.setMessage(authnRequest);
SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
endpointContext.setEndpoint(URLToEndpoint("https://192.168.1.14:8443/360.suite/loginSAML.xhtml"));
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.setProperty("resource.loader", "classpath");
velocityEngine.setProperty("classpath.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
velocityEngine.init();
HTTPPostEncoder encoder = new HTTPPostEncoder();
encoder.setVelocityEngine(velocityEngine);
encoder.setMessageContext(context);
encoder.setHttpServletResponse(response);
encoder.initialize();
encoder.encode();
}
public String doSAMLLogon(HttpServletRequest request, HttpServletResponse response) {
isLogged = true;
technicalConfigurationBean.init();
return loginBean.generateSSOSession(request, technicalConfigurationBean.getSsoPreferences(),
new SamlSSO(technicalConfigurationBean.getCmsPreferences().getCms()));
}
private NameIDPolicy buildNameIdPolicy() {
NameIDPolicy nameIDPolicy = OpenSAMLUtils.buildSAMLObject(NameIDPolicy.class);
nameIDPolicy.setAllowCreate(true);
nameIDPolicy.setFormat(NameIDType.TRANSIENT);
return nameIDPolicy;
}
private Endpoint URLToEndpoint(String URL) {
SingleSignOnService endpoint = OpenSAMLUtils.buildSAMLObject(SingleSignOnService.class);
endpoint.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
endpoint.setLocation(URL);
return endpoint;
}
private Issuer buildIssuer() {
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(entityId);
return issuer;
}
}
The redirect is successfully processed and the IDP sends back a POST request to my application that call this code :
#Override
public IEnterpriseSession logon(HttpServletRequest request) throws SDKException, Three60Exception {
HTTPPostDecoder decoder = new HTTPPostDecoder();
decoder.setHttpServletRequest(request);
AuthnRequest authnRequest;
try {
decoder.initialize();
decoder.decode();
MessageContext messageContext = decoder.getMessageContext();
authnRequest = (AuthnRequest) messageContext.getMessage();
OpenSAMLUtils.logSAMLObject(authnRequest);
// Here I Need the user
String user = authnRequest.getSubject().getNameID().getValue();
// BOBJ SDK
String secret = TrustedSso.getSecret();
ISessionMgr sm = CrystalEnterprise.getSessionMgr();
final ITrustedPrincipal trustedPrincipal = sm.createTrustedPrincipal(user, cms, secret);
return sm.logon(trustedPrincipal);
} catch (ComponentInitializationException | MessageDecodingException e) {
return null;
}
}
The issue here is that getSubject() is null on this query.
What did I miss here? Do I need to perform other requests? Do I need to add other configuration in my AuthnRequest?
As stated in the comment, I found the reason why my code was not working.
As I also asked this question on a french forum, can can find the translation of this answer here.
Short answer :
Opensaml knows where to send the authn request thanks to the SAMLPeerEntityContext. In my code I put my own application as the target of this request instead of using the idp HTTP-POST bind endpoint. Once this was changed, everything worked, the idp was answering back the SAMLResponse with proper name.
Long version
On my code, I was building the entity context like this :
SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
endpointContext.setEndpoint(URLToEndpoint("https://192.168.1.14:8443/360.suite/loginSAML.xhtml"));
This code forces the authn request to be sent to my own application instead of the IDP. As this is the request, it cannot contain the identity.
If I replace this URL by idpEndpoint which I got from the IDP metadata file, the full workflow works as expected.
First something will not work as my IDP forces requests to be signed, so I need to add a signature part.
The "signing and verification" sample of this repository just works for that.
Then, as I need a real identity, I must NOT ask for a transient nameid. In my tests, UNSPECIFIED worked, but PERSISTENT should also make it.
Last, in the ACS receiver, I do NOT receive an authn request but a SAMLResponse with assertions. The code will therefore look like :
String userName =
((ResponseImpl) messageContext.getMessage()).getAssertions().get(0).getSubject().getNameID()
.getValue();
I simplified the code but one, of course, has to check that :
(((ResponseImpl)messageContext.getMessage()).getStatus() is SUCCESS
signatures are valid
assertions are properly populated
Thanks #identigral for your answer in the comment

Adding a custom http header to a spring boot WS call (wstemplate)

I followed this guide "Consuming a SOAP web service", at
https://spring.io/guides/gs/consuming-web-service/
and changed it to call my own internal SOAP service, it makes the call
as expected, however now I need to pass an http header via the WsTemplate,
what is the easiest way to do this?
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
String headerKey="headerkey";
String headerValue="headervalue";
addRequestHeader(headerKey, headerValue);
}
private void addRequestHeader(String headerKey, String headerValue) throws IOException
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpUrlConnection) {
HttpUrlConnection conn = (HttpUrlConnection) connection;
conn.addRequestHeader(headerKey, headerValue);
}
}
}
I'm not sure if this helps but found some documentation
For setting WS-Addressing headers on the client, you can use the org.springframework.ws.soap.addressing.client.ActionCallback. ...
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
I've faced the same problem. If it can help someone, I've found a solution here: Spring WS Add Soap Header in Client
The idea is to create a class implementing org.springframework.ws.client.core.WebServiceMessageCallback and override the doWithMessage() method.
The doItMessage() method takes a WebServiceMessage as argument and is invoqued by the springWs process before sending the request, allowing to modify it before it is send.
What is done in the exemple above is marschalling the object and adding it to the header of the request.
In my case I have to be carefull with XML annotions of the object to be set as header, especially the #XmlRootElement with the namespace attribute.
Once this is done, the WSClient has to be adjusted to use the marshalSendAndReceive() method that takes a request and an uri, a payload object, and a WebServiceMessageCallback.

How to extract SOAP header from a WS response using spring-ws and jaxb

We're using spring-ws 2.2 on our application to consume web services. We've been happily doing this for quite some time and everything is working fine, except that now I need to access the SOAP header in the response and I just can't find a way to do this.
We are using a WebServiceTemplate (from springs-ws) configured with a Jaxb2Marshaller. The jaxb files are generated from the wsdl using xjc. The header element in my responses look something like this:
<soapenv:Header>
<v1:ResponseHeader status="OK">
<v1:description>test</v1:description>
</v1:ResponseHeader>
</soapenv:Header>
In my java class, the code that parses the response looks like this (I've stripped some irrelevant code):
public CalculationData getValues(Integer id) throws IntegrationException {
WebServiceMessageCallback callback = createCallback(soapAction);
GetValuesRequest request = toGetValues(id);
GetValuesResponse response = null;
try {
response = (GetValuesResponse) webServiceTemplate.marshalSendAndReceive(request, callback);
} catch (SOAPFaultException fault) {
log.error("Soap fault occurred during getValues " + id);
throw new IntegrationException(fault);
}
CalculationData data = fromGetValues(response);
return data;
}
Please help me find a solution for extracting the information from the SOAP header out of the response. I must be able to parse the status code which is sent as an attribute.
By the way. I also have a ResponseHeader.java jaxb class which has been generated from the schemas.
Update from final changes:
This is how my handleResponse method looks like after inlining a ClientInterceptor implementation:
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> responseHeaderElements =
message.getSoapHeader().examineAllHeaderElements();
SoapHeaderElement header = null;
if (responseHeaderElements.hasNext()) {
header = responseHeaderElements.next();
} else {
log.error("Error! No ResponseHeader found in response.");
return false;
}
String responseCode = header.getAttributeValue(new QName(STATUS_QNAME));
responseMsg.put(RESPONSE_MSG_KEY, responseCode);
return true;
}
I tried getting the ResponseHeader element by QName, but that did not seem to work for some reason. However, I only expect to get one element in the soap header anyhow, is this will work fine.
For this use case, the best solution is to use a custom WebServiceMessageExtractor, as described here:
http://veithen.github.io/2015/01/03/spring-ws-extracting-soap-headers-from-responses.html
Implement a ClientInterceptor, specifically see handleResponse() method.
In order to access the Soap Headers, convert to a SoapMessage
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
QName v1ResponseHeaderQName = null;//todo
QName statusAttrQName = null;//todo
SoapMessage message = (SoapMessage) messageContext.getResponse();
Iterator<SoapHeaderElement> matchingHeaders = message.getSoapHeader().examineHeaderElements(v1ResponseHeaderQName);
String status = matchingHeaders.next().getAttributeValue(statusAttrQName);
}
then call webServiceTemplate.setInterceptors(..)
For some further examples of this stuff see AbstractWsSecurityInterceptor and its subclasses. Be aware however that those interceptors deal with replacing the request message, you just want to read the response message.
Problem is you are dealing with the raw soap message now so you've lost the nice spring marshalling and need to start dealing with namespaces (QNames) and w3c Dom stuff.
In order for the interceptor to pass the header back to the calling code, you could make the interceptor an anonymous inner class that is setup inside your getValues(...) method.
final Map<String,String> headers = new HashMap<>();
template.setInterceptors(new ClientInterceptor[]{new ClientInterceptor() {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
headers.put("foo", "bar");
return false;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
}});
template.marshalSendAndReceive(....);

How can I associate and inbound and outbound soaphandler with each-other

http://docs.oracle.com/javaee/5/api/javax/xml/ws/handler/soap/SOAPHandler.html
How can I associate an inbound handleMessage(SOAPMessageContext) call with and outbound handleMessage(SOAPMessageContext).
I've tried a few things, Weblogic doesn't reuse the context object so reference checking doesn't work. I can't find any property that indicates the request distinctly and therefor I can't seem to create a link between the request and response.
Alternatively is there a better way to get the request and response on web-logic problematically and associated together such that they can be dumped to the database for future debugging purposes.
Okay, so this may not work on all implementations of JAX-WS but for weblogic this certainly does.
What I've done
public final boolean handleMessage(SOAPMessageContext context) {
// Using `Object` type as we don't have any need to import servlet libraries into a JAX-WS library
Object o = context.get(MessageContext.SERVLET_REQUEST);
// o will hold a reference to the request
// if inbound.o == outbound.o the request objects are identical and therefor we can associate them.
return true;
}
I don't see why this wouldn't work in other containers but please double check before using this method.
This requires a bit of boiler plate code, but it's not dependent on an app server.
The AttachAttributesFilter adds a startTime and UUID to the attributes. These attributes are then read in LogSoapHandler for inbound and outbound messages.
A simple search in the log will show the input and output for a specific UUID.
#WebFilter("/yourwebservice")
public class AttachAttributesFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setAttribute("STARTTIME", System.currentTimeMillis());
request.setAttribute("UUID",java.util.UUID.randomUUID().toString());
chain.doFilter(request, response);
}
}
Then I use the attributes in a LogSoapHander
public class LogSoapHandler implements
javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext messagecontext) {
Boolean outbound = (Boolean) messagecontext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
String messageID = (String) httpSR.getAttribute("UUID");
Object startTime = httpSR.getAttribute("STARTTIME");
try {
final SOAPMessage message = messagecontext.getMessage();
String encoding = getMessageEncoding(message);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean fault = (message.getSOAPBody().hasFault());
message.writeTo(baos);
String body = (baos.toString(encoding));
log.info(outbound+"|"+messageID+"|"+startTime+"|"+System.currentTimeMillis()+"|"+body+"|"+fault));
} catch (SOAPException | IOException ex) {
//handle your error
}
return true;
}
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING)
.toString();
}
return encoding;
}
And to be complete the HandlerChain boilder plate :
#HandlerChain(file="loghandler.xml")
public class MyWSDL {
..
}
loghandler.xml :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-class>xxx.LogSoapHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>

Adding elements in SOAP Header request for authentication

I need to incorporate an authentication header (i.e. as a part of SOAP header request) in my new web service. That authentication header will verify the userId and password details. I have to verify the content of request header details for authentication in my Web Service. If authenticated, then the SOAP body of the request will be processed, else Invalid Authentication message will be send back by the Web Service to the client application invoking the service.
I am not able to understand how to create a web service where the SOAP Header will contain some elements(in my case, authentication elements such as userId and password).
Normally, whatever method exposed in the service will come as a part of the SOAP Body. Hence confused how to proceed with adding authentication elements in the SOAP Header.
Please help
Regards,
Recently I have wrote a class which adds user credentials to SOAP header. To do that you need to create a class which implements SOAPHandler<SOAPMessageContext> interface. For e.g.:
public class MyHandler implements SOAPHandler<SOAPMessageContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
private String username;
private String password;
/**
* Handles SOAP message. If SOAP header does not already exist, then method will created new SOAP header. The
* username and password is added to the header as the credentials to authenticate user. If no user credentials is
* specified every call to web service will fail.
*
* #param context SOAP message context to get SOAP message from
* #return true
*/
#Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage message = context.getMessage();
SOAPHeader header = message.getSOAPHeader();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
if (header == null) {
header = envelope.addHeader();
}
QName qNameUserCredentials = new QName("https://your.target.namespace/", "UserCredentials");
SOAPHeaderElement userCredentials = header.addHeaderElement(qNameUserCredentials);
QName qNameUsername = new QName("https://your.target.namespace/", "Username");
SOAPHeaderElement username = header.addHeaderElement(qNameUsername );
username.addTextNode(this.username);
QName qNamePassword = new QName("https://your.target.namespace/", "Password");
SOAPHeaderElement password = header.addHeaderElement(qNamePassword);
password.addTextNode(this.password);
userCredentials.addChildElement(username);
userCredentials.addChildElement(password);
message.saveChanges();
//TODO: remove this writer when the testing is finished
StringWriter writer = new StringWriter();
message.writeTo(new StringOutputStream(writer));
LOGGER.debug("SOAP message: \n" + writer.toString());
} catch (SOAPException e) {
LOGGER.error("Error occurred while adding credentials to SOAP header.", e);
} catch (IOException e) {
LOGGER.error("Error occurred while writing message to output stream.", e);
}
return true;
}
//TODO: remove this class after testing is finished
private static class StringOutputStream extends OutputStream {
private StringWriter writer;
public StringOutputStream(StringWriter writer) {
this.writer = writer;
}
#Override
public void write(int b) throws IOException {
writer.write(b);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
LOGGER.debug("handleFault has been invoked.");
return true;
}
#Override
public void close(MessageContext context) {
LOGGER.debug("close has been invoked.");
}
#Override
public Set<QName> getHeaders() {
LOGGER.debug("getHeaders has been invoked.");
return null;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
Note that I am just adding the credentials to the header and returning true. You do what ever you want with whole message and return false if something that is expected fails.
I have implemented this one the client:
<bean id="soapHandler" class="your.package.MyHandler">
<property name="username" value="testUser"/>
<property name="password" value="testPassword"/>
</bean>
<jaxws:client "...">
<jaxws:handlers>
<ref bean="soapHandler"/>
</jaxws:handlers>
</jaxws:client>
But it also can be implemented on the endpoint.
We can get header from the envelop only not from soap message.

Categories