You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by "David Roytenberg (Consultant)" <Da...@optimalpayments.com> on 2014/09/18 15:54:24 UTC

Best Practice for Passing SOAP Header Credentials Dynamically?

I have created a SOAP client to an external web service using CXF 2.7.6
with a WSDL-first approach.  I've generated my client classes and used
Spring to instantiate my SOAP client.

 

I am required to pass a username and password for injection into the
SOAP headers, and it is necessary to pick up that username and password
dynamically for each web service call.  In the client code the SOAP
headers are not yet accessible.  I wrote an Interceptor to populate the
SOAP headers and used DOM to do it.

 

To make the parameters available to my Interceptor, I used the CXF
Request Context.  According to the documentation this can be made thread
safe by setting thread.local.request.context to true.

 

My question is whether this could result in a performance bottlekneck.
Does this result in all messages synchronizing against the same request
context object?   If so, is there another approach in CXF that would
avoid this?

 

 

In the CXF client I put the user name and password into the CXF context
like this:

 

private void passUserNameAndPassword(String userName, String password) {

    Client cxfClient = ClientProxy.getClient(myClient);

    // Per CXF documentation the following setting will make the context
thread safe:

 
if(!"true".equals(cxfClient.getRequestContext().get("thread.local.reques
t.context"))) {

      cxfClient.getRequestContext().put("thread.local.request.context",
"true");

    }

    // Now we can pass the credentials safely

    cxfClient.getRequestContext().put("userName", userName);

   cxfClient.getRequestContext().put("password", password);

  }

 

 

My interceptor then pulls it out and puts it into the SOAP headers.  I
include the whole thing here in case it is useful to others.

 

import java.util.Iterator;

import java.util.List;

import java.util.Map.Entry;

import java.util.Set;

 

import javax.xml.namespace.QName;

 

import org.apache.cxf.binding.soap.SoapHeader;

import org.apache.cxf.binding.soap.SoapMessage;

import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;

import
org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;

import org.apache.cxf.headers.Header;

import org.apache.cxf.helpers.DOMUtils;

import org.apache.cxf.interceptor.Fault;

import org.apache.cxf.phase.Phase;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

 

public class SoapHeadersInterceptor extends AbstractSoapInterceptor

{

  private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory

      .getLogger(SoapHeadersInterceptor.class);

 

   public SoapHeadersInterceptor()

  {

    super(Phase.PRE_STREAM);

    addBefore(SoapPreProtocolOutInterceptor.class.getName());

  }

 

 

  @Override

  public void handleMessage(SoapMessage msg) throws Fault

  {

    logger.info("Called SoapHeadersInterceptor.handleMessage()!");

    logger.info("SoapMessage enumeration");

    Set<Entry<String,Object>> entrySet =msg.entrySet();

    Iterator <Entry<String,Object>> sit = entrySet.iterator();

    String userName=null;

    String password=null;

    while(sit.hasNext()) {

      Entry<String,Object> nextEntry= sit.next();

      logger.info("Entry key:  "+nextEntry.getKey()+", value:
"+nextEntry.getValue());

      

      if("userName".equals(nextEntry.getKey())) {

        userName=(String)nextEntry.getValue();

      }

      if("password".equals(nextEntry.getKey())) {

        password=(String)nextEntry.getValue();

      }        

    }

 

    List<Header> headers = msg.getHeaders();

 

    Document d = DOMUtils.createDocument();

    Element userNameElement = d.createElement("wsse:Username");

    userNameElement.setTextContent(userName);

    logger.info("Setting userName to " + userName);

    Element passwordElement = d.createElement("wsse:Password");

    passwordElement.setTextContent(password);

    logger.debug("Setting password to " + password);

    Element token = d.createElement("wsse:UsernameToken");

    Element security = d.createElementNS(

 
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
xt-1.0.xsd",

        "wsse:Security");

    security.appendChild(token);

    token.appendChild(userNameElement);

    token.appendChild(passwordElement);

 

    SoapHeader securityHeader = new SoapHeader(new QName(

 
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
xt-1.0.xsd",

        "wsse:Security"), security);

    securityHeader.setMustUnderstand(true);

    headers.add(securityHeader);

  }

 

}

 

Thanks for any insight you can offer.

 

David

-- 
WARNING
-------
This electronic message and its attachments may contain confidential, proprietary or legally privileged information, which is solely for the use of the intended recipient.  No privilege or other rights are waived by any unintended transmission or unauthorized retransmission of this message.  If you are not the intended recipient of this message, or if you have received it in error, you should immediately stop reading this message and delete it and all attachments from your system.  The reading, distribution, copying or other use of this message or its attachments by unintended recipients is unauthorized and may be unlawful.  If you have received this e-mail in error, please notify the sender.

AVIS IMPORTANT
--------------
Ce message electronique et ses pieces jointes peuvent contenir des renseignements confidentiels, exclusifs ou legalement privilegies destines au seul usage du destinataire vise.  L'expediteur original ne renonce a aucun privilege ou a aucun autre droit si le present message a ete transmis involontairement ou s'il est retransmis sans son autorisation.  Si vous n'etes pas le destinataire vise du present message ou si vous l'avez recu par erreur, veuillez cesser immediatement de le lire et le supprimer, ainsi que toutes ses pieces jointes, de votre systeme.  La lecture, la distribution, la copie ou tout autre usage du present message ou de ses pieces jointes par des personnes autres que le destinataire vise ne sont pas autorises et pourraient etre illegaux.  Si vous avez recu ce courrier electronique par erreur, veuillez en aviser l'expediteur.


RE: Best Practice for Passing SOAP Header Credentials Dynamically?

Posted by Andrei Shakirin <as...@talend.com>.
Hi,

Do you have a good reasons not to use standard UsernameToken headers:
https://www.oasis-open.org/committees/download.php/16782/wss-v1.1-spec-os-UsernameTokenProfile.pdf 
https://web-gmazza.rhcloud.com/blog/entry/cxf-usernametoken-profile  ?.

It is supported by CXF out of the box.

To your question regarding thread.local.request.context: I don't think that it has significant performance influence, because CXF doesn't synchronizes calls, it just stores request parameters into thread local areas.

Regards,
Andrei.
 
> -----Original Message-----
> From: David Roytenberg (Consultant)
> [mailto:David.Roytenberg@optimalpayments.com]
> Sent: Donnerstag, 18. September 2014 15:54
> To: users@cxf.apache.org
> Subject: Best Practice for Passing SOAP Header Credentials Dynamically?
> 
> I have created a SOAP client to an external web service using CXF 2.7.6 with a
> WSDL-first approach.  I've generated my client classes and used Spring to
> instantiate my SOAP client.
> 
> 
> 
> I am required to pass a username and password for injection into the SOAP
> headers, and it is necessary to pick up that username and password
> dynamically for each web service call.  In the client code the SOAP headers are
> not yet accessible.  I wrote an Interceptor to populate the SOAP headers and
> used DOM to do it.
> 
> 
> 
> To make the parameters available to my Interceptor, I used the CXF Request
> Context.  According to the documentation this can be made thread safe by
> setting thread.local.request.context to true.
> 
> 
> 
> My question is whether this could result in a performance bottlekneck.
> Does this result in all messages synchronizing against the same request
> context object?   If so, is there another approach in CXF that would
> avoid this?
> 
> 
> 
> 
> 
> In the CXF client I put the user name and password into the CXF context like
> this:
> 
> 
> 
> private void passUserNameAndPassword(String userName, String password) {
> 
>     Client cxfClient = ClientProxy.getClient(myClient);
> 
>     // Per CXF documentation the following setting will make the context thread
> safe:
> 
> 
> if(!"true".equals(cxfClient.getRequestContext().get("thread.local.reques
> t.context"))) {
> 
>       cxfClient.getRequestContext().put("thread.local.request.context",
> "true");
> 
>     }
> 
>     // Now we can pass the credentials safely
> 
>     cxfClient.getRequestContext().put("userName", userName);
> 
>    cxfClient.getRequestContext().put("password", password);
> 
>   }
> 
> 
> 
> 
> 
> My interceptor then pulls it out and puts it into the SOAP headers.  I include the
> whole thing here in case it is useful to others.
> 
> 
> 
> import java.util.Iterator;
> 
> import java.util.List;
> 
> import java.util.Map.Entry;
> 
> import java.util.Set;
> 
> 
> 
> import javax.xml.namespace.QName;
> 
> 
> 
> import org.apache.cxf.binding.soap.SoapHeader;
> 
> import org.apache.cxf.binding.soap.SoapMessage;
> 
> import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
> 
> import
> org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
> 
> import org.apache.cxf.headers.Header;
> 
> import org.apache.cxf.helpers.DOMUtils;
> 
> import org.apache.cxf.interceptor.Fault;
> 
> import org.apache.cxf.phase.Phase;
> 
> import org.w3c.dom.Document;
> 
> import org.w3c.dom.Element;
> 
> 
> 
> public class SoapHeadersInterceptor extends AbstractSoapInterceptor
> 
> {
> 
>   private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory
> 
>       .getLogger(SoapHeadersInterceptor.class);
> 
> 
> 
>    public SoapHeadersInterceptor()
> 
>   {
> 
>     super(Phase.PRE_STREAM);
> 
>     addBefore(SoapPreProtocolOutInterceptor.class.getName());
> 
>   }
> 
> 
> 
> 
> 
>   @Override
> 
>   public void handleMessage(SoapMessage msg) throws Fault
> 
>   {
> 
>     logger.info("Called SoapHeadersInterceptor.handleMessage()!");
> 
>     logger.info("SoapMessage enumeration");
> 
>     Set<Entry<String,Object>> entrySet =msg.entrySet();
> 
>     Iterator <Entry<String,Object>> sit = entrySet.iterator();
> 
>     String userName=null;
> 
>     String password=null;
> 
>     while(sit.hasNext()) {
> 
>       Entry<String,Object> nextEntry= sit.next();
> 
>       logger.info("Entry key:  "+nextEntry.getKey()+", value:
> "+nextEntry.getValue());
> 
> 
> 
>       if("userName".equals(nextEntry.getKey())) {
> 
>         userName=(String)nextEntry.getValue();
> 
>       }
> 
>       if("password".equals(nextEntry.getKey())) {
> 
>         password=(String)nextEntry.getValue();
> 
>       }
> 
>     }
> 
> 
> 
>     List<Header> headers = msg.getHeaders();
> 
> 
> 
>     Document d = DOMUtils.createDocument();
> 
>     Element userNameElement = d.createElement("wsse:Username");
> 
>     userNameElement.setTextContent(userName);
> 
>     logger.info("Setting userName to " + userName);
> 
>     Element passwordElement = d.createElement("wsse:Password");
> 
>     passwordElement.setTextContent(password);
> 
>     logger.debug("Setting password to " + password);
> 
>     Element token = d.createElement("wsse:UsernameToken");
> 
>     Element security = d.createElementNS(
> 
> 
> "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
> xt-1.0.xsd",
> 
>         "wsse:Security");
> 
>     security.appendChild(token);
> 
>     token.appendChild(userNameElement);
> 
>     token.appendChild(passwordElement);
> 
> 
> 
>     SoapHeader securityHeader = new SoapHeader(new QName(
> 
> 
> "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
> xt-1.0.xsd",
> 
>         "wsse:Security"), security);
> 
>     securityHeader.setMustUnderstand(true);
> 
>     headers.add(securityHeader);
> 
>   }
> 
> 
> 
> }
> 
> 
> 
> Thanks for any insight you can offer.
> 
> 
> 
> David
> 
> --
> WARNING
> -------
> This electronic message and its attachments may contain confidential,
> proprietary or legally privileged information, which is solely for the use of the
> intended recipient.  No privilege or other rights are waived by any unintended
> transmission or unauthorized retransmission of this message.  If you are not the
> intended recipient of this message, or if you have received it in error, you
> should immediately stop reading this message and delete it and all
> attachments from your system.  The reading, distribution, copying or other use
> of this message or its attachments by unintended recipients is unauthorized and
> may be unlawful.  If you have received this e-mail in error, please notify the
> sender.
> 
> AVIS IMPORTANT
> --------------
> Ce message electronique et ses pieces jointes peuvent contenir des
> renseignements confidentiels, exclusifs ou legalement privilegies destines au
> seul usage du destinataire vise.  L'expediteur original ne renonce a aucun
> privilege ou a aucun autre droit si le present message a ete transmis
> involontairement ou s'il est retransmis sans son autorisation.  Si vous n'etes pas
> le destinataire vise du present message ou si vous l'avez recu par erreur,
> veuillez cesser immediatement de le lire et le supprimer, ainsi que toutes ses
> pieces jointes, de votre systeme.  La lecture, la distribution, la copie ou tout
> autre usage du present message ou de ses pieces jointes par des personnes
> autres que le destinataire vise ne sont pas autorises et pourraient etre illegaux.
> Si vous avez recu ce courrier electronique par erreur, veuillez en aviser
> l'expediteur.


Re: Best Practice for Passing SOAP Header Credentials Dynamically?

Posted by Daniel Kulp <dk...@apache.org>.
Question:

Is the soap header that holds the username/password also defined in the WSDL?   Does the SOAP binding properly mark that header as a soap header for those methods?   If so, you COULD just have it generate the interface with the username and password parameters added to the methods.    Would be fairly simple then.

In your interceptor, I wouldn’t iterate over the msg.   Just call msg.get(“username”), msg.get(“password”), etc…     

That said, you are just using a normal WS-Security UsernamePassword setup.   CXF already has good support for that built in.    See http://cxf.apache.org/docs/ws-security.html.

Dan




On Sep 18, 2014, at 9:54 AM, David Roytenberg (Consultant) <Da...@optimalpayments.com> wrote:

> I have created a SOAP client to an external web service using CXF 2.7.6
> with a WSDL-first approach.  I've generated my client classes and used
> Spring to instantiate my SOAP client.
> 
> 
> 
> I am required to pass a username and password for injection into the
> SOAP headers, and it is necessary to pick up that username and password
> dynamically for each web service call.  In the client code the SOAP
> headers are not yet accessible.  I wrote an Interceptor to populate the
> SOAP headers and used DOM to do it.
> 
> 
> 
> To make the parameters available to my Interceptor, I used the CXF
> Request Context.  According to the documentation this can be made thread
> safe by setting thread.local.request.context to true.
> 
> 
> 
> My question is whether this could result in a performance bottlekneck.
> Does this result in all messages synchronizing against the same request
> context object?   If so, is there another approach in CXF that would
> avoid this?
> 
> 
> 
> 
> 
> In the CXF client I put the user name and password into the CXF context
> like this:
> 
> 
> 
> private void passUserNameAndPassword(String userName, String password) {
> 
>    Client cxfClient = ClientProxy.getClient(myClient);
> 
>    // Per CXF documentation the following setting will make the context
> thread safe:
> 
> 
> if(!"true".equals(cxfClient.getRequestContext().get("thread.local.reques
> t.context"))) {
> 
>      cxfClient.getRequestContext().put("thread.local.request.context",
> "true");
> 
>    }
> 
>    // Now we can pass the credentials safely
> 
>    cxfClient.getRequestContext().put("userName", userName);
> 
>   cxfClient.getRequestContext().put("password", password);
> 
>  }
> 
> 
> 
> 
> 
> My interceptor then pulls it out and puts it into the SOAP headers.  I
> include the whole thing here in case it is useful to others.
> 
> 
> 
> import java.util.Iterator;
> 
> import java.util.List;
> 
> import java.util.Map.Entry;
> 
> import java.util.Set;
> 
> 
> 
> import javax.xml.namespace.QName;
> 
> 
> 
> import org.apache.cxf.binding.soap.SoapHeader;
> 
> import org.apache.cxf.binding.soap.SoapMessage;
> 
> import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
> 
> import
> org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
> 
> import org.apache.cxf.headers.Header;
> 
> import org.apache.cxf.helpers.DOMUtils;
> 
> import org.apache.cxf.interceptor.Fault;
> 
> import org.apache.cxf.phase.Phase;
> 
> import org.w3c.dom.Document;
> 
> import org.w3c.dom.Element;
> 
> 
> 
> public class SoapHeadersInterceptor extends AbstractSoapInterceptor
> 
> {
> 
>  private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory
> 
>      .getLogger(SoapHeadersInterceptor.class);
> 
> 
> 
>   public SoapHeadersInterceptor()
> 
>  {
> 
>    super(Phase.PRE_STREAM);
> 
>    addBefore(SoapPreProtocolOutInterceptor.class.getName());
> 
>  }
> 
> 
> 
> 
> 
>  @Override
> 
>  public void handleMessage(SoapMessage msg) throws Fault
> 
>  {
> 
>    logger.info("Called SoapHeadersInterceptor.handleMessage()!");
> 
>    logger.info("SoapMessage enumeration");
> 
>    Set<Entry<String,Object>> entrySet =msg.entrySet();
> 
>    Iterator <Entry<String,Object>> sit = entrySet.iterator();
> 
>    String userName=null;
> 
>    String password=null;
> 
>    while(sit.hasNext()) {
> 
>      Entry<String,Object> nextEntry= sit.next();
> 
>      logger.info("Entry key:  "+nextEntry.getKey()+", value:
> "+nextEntry.getValue());
> 
> 
> 
>      if("userName".equals(nextEntry.getKey())) {
> 
>        userName=(String)nextEntry.getValue();
> 
>      }
> 
>      if("password".equals(nextEntry.getKey())) {
> 
>        password=(String)nextEntry.getValue();
> 
>      }        
> 
>    }
> 
> 
> 
>    List<Header> headers = msg.getHeaders();
> 
> 
> 
>    Document d = DOMUtils.createDocument();
> 
>    Element userNameElement = d.createElement("wsse:Username");
> 
>    userNameElement.setTextContent(userName);
> 
>    logger.info("Setting userName to " + userName);
> 
>    Element passwordElement = d.createElement("wsse:Password");
> 
>    passwordElement.setTextContent(password);
> 
>    logger.debug("Setting password to " + password);
> 
>    Element token = d.createElement("wsse:UsernameToken");
> 
>    Element security = d.createElementNS(
> 
> 
> "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
> xt-1.0.xsd",
> 
>        "wsse:Security");
> 
>    security.appendChild(token);
> 
>    token.appendChild(userNameElement);
> 
>    token.appendChild(passwordElement);
> 
> 
> 
>    SoapHeader securityHeader = new SoapHeader(new QName(
> 
> 
> "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-sece
> xt-1.0.xsd",
> 
>        "wsse:Security"), security);
> 
>    securityHeader.setMustUnderstand(true);
> 
>    headers.add(securityHeader);
> 
>  }
> 
> 
> 
> }
> 
> 
> 
> Thanks for any insight you can offer.
> 
> 
> 
> David
> 
> -- 
> WARNING
> -------
> This electronic message and its attachments may contain confidential, proprietary or legally privileged information, which is solely for the use of the intended recipient.  No privilege or other rights are waived by any unintended transmission or unauthorized retransmission of this message.  If you are not the intended recipient of this message, or if you have received it in error, you should immediately stop reading this message and delete it and all attachments from your system.  The reading, distribution, copying or other use of this message or its attachments by unintended recipients is unauthorized and may be unlawful.  If you have received this e-mail in error, please notify the sender.
> 
> AVIS IMPORTANT
> --------------
> Ce message electronique et ses pieces jointes peuvent contenir des renseignements confidentiels, exclusifs ou legalement privilegies destines au seul usage du destinataire vise.  L'expediteur original ne renonce a aucun privilege ou a aucun autre droit si le present message a ete transmis involontairement ou s'il est retransmis sans son autorisation.  Si vous n'etes pas le destinataire vise du present message ou si vous l'avez recu par erreur, veuillez cesser immediatement de le lire et le supprimer, ainsi que toutes ses pieces jointes, de votre systeme.  La lecture, la distribution, la copie ou tout autre usage du present message ou de ses pieces jointes par des personnes autres que le destinataire vise ne sont pas autorises et pourraient etre illegaux.  Si vous avez recu ce courrier electronique par erreur, veuillez en aviser l'expediteur.
> 

-- 
Daniel Kulp
dkulp@apache.org - http://dankulp.com/blog
Talend Community Coder - http://coders.talend.com