I'd rather configure passwords for my client that uses CXF via Spring, but the use of CallbackHandler that seems it must be instantiated via reflection makes this more difficult.

While I understand that some may want to store their password into a hardcoded CallbackHandler class or even maybe just set passwords statically into that class directly, is there another simpler way than the example I wrote below (which is a simpified version of what I'm currently using) to specify a username and password (and endpoint, even though it isn't listed here) via Spring on the instance of the client?

Thanks in advance!
Gary

--- start example code MyClient.java ---

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;

import java.util.HashMap;
import java.util.Map;


public class MyClient {

   private String endpoint;
   private String user;
   private String password;

   private Log log = LogFactory.getLog(MyClient.class);

   protected void testService() {

       MyService service = new MyService(null);
       MyServicePortType portType = service.getMyServicePort();
       Client client = ClientProxy.getClient(portType);

       // set username and password
       // see: http://cwiki.apache.org/CXF20DOC/ws-security.html
       Endpoint clientEndpoint = client.getEndpoint();
       // No security on client-side
       Map<String, Object> inProps = new HashMap<String, Object>();
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.NO_SECURITY);
       WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
       clientEndpoint.getInInterceptors().add(wssIn);
clientEndpoint.getInInterceptors().add(new SAAJInInterceptor()); // 2.0.x only; not needed in 2.1+
       // Server-side authN
       Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
       outProps.put(WSHandlerConstants.USER, getUser());
       outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
//outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);

// TODO: Is there a cleaner way of doing this that will still allow Spring to configure the password?
       setStaticPasswordProviderOnCallbackHandler(this);
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, getCallbackHandlerClassName());

       WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
       clientEndpoint.getOutInterceptors().add(wssOut);
clientEndpoint.getOutInterceptors().add(new SAAJOutInterceptor()); // 2.0.x only; not needed in 2.1+

MyServiceResponse response = portType.someServiceMethod(MyServiceRequest);
       return response;
   }

   public String getEndpoint() {
       return endpoint;
   }

   public void setEndpoint(String endpoint) {
       this.endpoint = endpoint;
   }

   public String getUser() {
       return user;
   }

   public void setUser(String user) {
       this.user = user;
   }

   public String getPassword() {
       return password;
   }

   public void setPassword(String password) {
       this.password = password;
   }
}

--- end example code MyClient.java ---

--- start example code MyCallbackHandler.java ---

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;


public class MyCallbackHandler implements CallbackHandler {

private static final Log log = LogFactory.getLog(MyCallbackHandler.class);

// this is ugly. However, has to be static so that it is available after instantiation via reflection as req'd
   private static MyClient passwordProvider;

   public void MyCallbackHandler() {
       log.info("MyCallbackHandler created");
   }

// Note: This class is instantiated via reflection, so we store the link to the password provider statically,
   // prior to instantiation. Don't really like this solution.
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

       WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

       // set the password for our message.
       if (passwordProvider!=null) {
           String password = passwordProvider.getPassword();

           if (password==null) {
log.warn("Got null password from provider " + passwordProvider);
           }

           pc.setPassword(password);
       }
       else {
throw new IOException("passwordProvider must be set on " + this.getClass().getName() + " before handle() can be called");
       }
   }

// this is ugly. However, has to be static so that it is available after instantiation via reflection as req'd public static void setStaticPasswordProvider(MyClient passwordProvider) { log.info("MyCallbackHandler static passwordProvider set to " + passwordProvider);
       MyCallbackHandler.passwordProvider = passwordProvider;
   }
}

--- end example code MyCallbackHandler.java ---


--
Gary Weaver
Internet Framework Services
Office of Information Technology
Duke University

Reply via email to