You guys must have known that if you just stayed quiet, eventually, I'd figure it out...
Again, the basic problem: Tomcat spawns different threads for different web service calls, so thread local storage of the security context doesn't really work across multiple calls during what amounts to the same (from the client standpoint) session.
In case anyone else ever runs up against this, here is the solution I came up with:
1) Add the HttpSessionContextIngrationFilter to the web.xml and cover your /services/* url with it.
2) Before making calls on the web service stub, call setMaintainSession(true) so it will use sessions (if anyone knows how to make this permanently true for a given web service, please let me know).
That's pretty much it. For those who haven't poured over the source for this filter, it basically looks on the current http session for an acegi security context. If it finds one, it sets it on the SecurityContextHolder, which has the effect of setting it on thread local storage for the current request. Exactly what we need here. If it doesn't find one, it creates a new one and sets it on the session.
So in my authenticate() method, I do the normal authentication and set that on the current security context, then I stick the context on the current session using the ACEGI_SECURITY_CONTEXT_KEY where the filter sees it.
The login method looks like this:
Once you have this working, just stick your web service code in a delegate bean, and cover that bean with a MethodSecurityInterceptor, and delegate all calls in your Axis service class to the delegate, and you're in business.
public void login( final String username, final String password ) throws AxisFault
// Create an authentication token from the credentials, and attempt to authenticate
final UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken( username, password );
final Authentication auth = m_authenticationManager.authenticate( token );
// If we make it here, no authentication errors occurred, so we set the
// authentication object on the security context, and then set that context
// as the current one for this thread. Note that we don't set the context on
// the holder, because below we stick it on the session, and the filter
// mentioned below will take care of sticking it in the holder on a per
// request basis.
final SecurityContext ctx = SecurityContextHolder.getContext();
ctx.setAuthentication( auth );
// Finally, we need to set this context on our http session, as
// the HttpSessionContextIntegrationFilter will be looking for it there,
// so it can maintain it on whatever thread is handling a request for
// this session
final AxisHttpSession session = ( AxisHttpSession )
final HttpSession httpSession = session.getRep();
Hope this helps someone...