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