I have also used WSS4J. This is an implementation of the OASIS spec for web service security. If you're using AXIS, this is an easy choice. You simply pass the password from ACEGI to the WSS4J password callback. If it is authenticated you can simply add the user to the SecurityContextHolder. This works really well, I use it to provide AOP Method level security on my business objects. If you use the UsernameToken authentication method, it passes the username in the xml, then a nonce, and the encrypted password.
Here is my callback handler integrated with ACEGI
Code:
public class ServiceAuthenticationCallbackHandler implements CallbackHandler {
/**
* Generic constructor
*
*/
public ServiceAuthenticationCallbackHandler() {
// empty constructor
}
/**
* Handle our incoming request chain
*/
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {
throw new UnsupportedCallbackException(
callbacks[i],
"The passwordType \"PasswordText\" is not supported. Please use passwordType \"PasswordDigest\"");
}
authenticate(MessageContext.getCurrentContext(), pc);
} else {
// if we get here the config is wrong
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
/**
* Call out the acegi security and put the credentials into the
* CurrentThread
*
* @param msgContext
* @param callback
* @throws IOException
*/
protected void authenticate(MessageContext msgContext,
WSPasswordCallback callback) throws IOException {
HttpServlet servlet = (HttpServlet) msgContext
.getProperty(HTTPConstants.MC_HTTP_SERVLET);
ApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(servlet.getServletContext());
AuthenticationProvider provider = (AuthenticationProvider) ctx
.getBean("authenticationProvider");
Authentication auth = new UsernamePasswordAuthenticationToken(callback
.getIdentifer(), callback);
// catch a wrong username and give the caller a hint
try {
provider.authenticate(auth);
} catch (BadCredentialsException e) {
throw new IOException(
"Could not autheticate with the given username and password");
}
SecurityContextHolder.getContext().setAuthentication(auth);
}
And my authentication DAO
Code:
/**
* Uses the access method to check the password against the <code>WSPasswordCallback</code>
* @author Todd Nine
*
*/
public class WebServiceSecurityDao extends DaoAuthenticationProvider {
/**
* Send the password to the authentication object
*/
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//TODO, this currently supports only a plaintext password
String password = userDetails.getPassword();
Object obj = authentication.getCredentials();
if(!(obj instanceof WSPasswordCallback)){
throw new WebServiceSecurityException("Principal is not an instance of " +WSPasswordCallback.class);
}
//set the callback. We don't need to check since the callback will do this.
((WSPasswordCallback)obj).setPassword(password);
}
}