I have written a custom RememberMeProcessingFilter to get cookie + concurrent session support with my application. Its a bit of a hack, so ill try to explain why I have done it this way, and hopefully someone can tell me if its ok or suggest a better way to do it (this is using acegi 1.00 RC1).
The problem I was having was this : the default RememberMeServices implementation - TokenBasedRememberMeServices does not check for concurrent user sessions, nor does it update the session registry when valid cookies are found. The AuthenticationManager 'ProviderManager' does these things, so I made it available to my own RememberMeProcessingFilter, and passed in the Authentication object (RememberMeAuthenticationToken) resulting from the TokenBasedRememberMeServices autoLogin method.
The ProviderManager sessionController - ConcurrentSessionControllerImpl - chokes on RememberMeAuthenticationTokens -- assertions fail in the SessionRegistryUtils. So I created a new UsernamePasswordAuthenticationToken and passed it to the ProviderManager instead.
Lastly I nabbed the unsuccessfulAuthentication method from AbstractProcessingFilter, so I could redirect the user back to the login page with an error if the maximum sessions were exceeded.
I'd be interested to hear from Ben or anyone if I am on the right track or completely off the rails 
Code:
package com.informa.mt.lmiu.security;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.acegisecurity.ui.rememberme.RememberMeServices;
import org.acegisecurity.ui.rememberme.NullRememberMeServices;
import org.acegisecurity.ui.WebAuthenticationDetails;
import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.userdetails.User;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* <p>
* </p>
*/
public class MyRememberMeProcessingFilter implements Filter, InitializingBean,
ApplicationEventPublisherAware {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(MyRememberMeProcessingFilter.class);
//~ Instance fields ========================================================
private ApplicationEventPublisher eventPublisher;
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private AuthenticationManager authenticationManager;
private String authenticationFailureUrl;
//~ Methods ================================================================
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
this.authenticationFailureUrl = authenticationFailureUrl;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(rememberMeServices);
}
/**
* Does nothing - we rely on IoC lifecycle services instead.
*/
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Can only process HttpServletResponse");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
Authentication rememberMeAuth =
rememberMeServices.autoLogin(httpRequest, httpResponse);
if(rememberMeAuth != null) {
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
/* check concurrent sessions */
/* arrgh */
User user = (User) rememberMeAuth.getPrincipal();
UsernamePasswordAuthenticationToken userPassAuth = new UsernamePasswordAuthenticationToken(
user,user.getPassword() , rememberMeAuth.getAuthorities()
);
userPassAuth.setDetails(new WebAuthenticationDetails(httpRequest));
// Place the last username attempted into HttpSession for views
httpRequest.getSession().setAttribute(
AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_USERNAME_KEY,user.getUsername());
try {
authenticationManager.authenticate(userPassAuth);
} catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(httpRequest, httpResponse, failed);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(
"SecurityContextHolder populated with remember-me token: '"
+ SecurityContextHolder.getContext().getAuthentication()
+ "'");
}
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
SecurityContextHolder.getContext().getAuthentication(),
this.getClass()));
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"SecurityContextHolder not populated with remember-me token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication()
+ "'");
}
}
chain.doFilter(request, response);
}
/**
* Stripped from AuthenticationProcessingFilter - redirects user to the failed URL
* @param request
* @param response
* @param failed
* @throws IOException
*/
private void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException {
SecurityContextHolder.getContext().setAuthentication(null);
if (logger.isDebugEnabled()) {
logger.debug(
"Updated SecurityContextHolder to contain null Authentication");
}
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString());
}
try {
request.getSession().setAttribute(
AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
} catch (Exception ignored) {}
rememberMeServices.loginFail(request, response);
response.sendRedirect(response.encodeRedirectURL(request.getContextPath()
+ authenticationFailureUrl));
}
/**
* Does nothing - we rely on IoC lifecycle services instead.
*
* @param ignored not used
*
*/
public void init(FilterConfig ignored) throws ServletException {}
}