如何在不重置tomcat的会话超时的情况下执行经过身份validation的AJAX请求?

我有一个正在生产的现有Grails Web应用程序,并且有30分钟的会话超时。 我们正在运行Tomcat(tcServer)。

当用户通过身份validation并在某些页面上我想要定期轮询对服务器的ajax请求,这些请求不会延长这个30分钟的会话超时 – 这样我们的会话超时就不会被阻止。

问题类似于这个未经回答的asp.net问题 ,但是在Java / Tomcat领域中没有任何答案可以做到这一点。

如何在不重置tomcat的会话超时的情况下执行经过身份validation的AJAX请求?

是否存在某种filter或URL匹配机制,我可以使用它来排除延长会话超时的请求?

我会使用Grailsfilter,它会执行类似于The-MeLLeR提议的内容,而不会在所有会话中进行不必要的循环:

class AjaxTimeoutFilters { int sessionTimeout = 30 * 60 * 1000 private static final String TIMEOUT_KEY = 'TIMEOUT_KEY' def filters = { all(controller:'*', action:'*') { before = { if (request.xhr) { Long lastAccess = session[TIMEOUT_KEY] if (lastAccess == null) { // TODO return false } if (System.currentTimeMillis() - lastAccess > sessionTimeout) { session.invalidate() // TODO - render response to trigger client redirect return false } } else { session[TIMEOUT_KEY] = System.currentTimeMillis() } true } } } } 

会话超时应该dependency injection或以其他方式与web.xml中的值保持同步。

还有两个问题。 一种情况是存在Ajax请求,但之前没有非Ajax请求(lastAccess == null)。 另一种方法是在没有非Ajax活动30分钟后,当有一个Ajax请求时,如何将浏览器重定向到登录页面或需要去的地方。 您必须呈现JSON或客户端将检查的其他响应,以确定它已超时并执行客户端重定向。

不可能……

一种选择如下:

1)创建一个javax.servlet.Filter并在会话中存储最后一个(非ajax)页面视图的时间戳。

2)创建一个javax.servlet.http.HttpSessionListener来存储所有活动会话。

3)使用后台线程使所有过期的会话无效。


示例代码:

 import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class LastAccessFilter implements Filter, HttpSessionListener { private static final Object SYNC_OBJECT = new Object(); private static final String LAST_ACCESSED = "lastAccessed"; private boolean backgroundThreadEnabled; public void destroy() { synchronized (SYNC_OBJECT){ backgroundThreadEnabled = false; SYNC_OBJECT.notifyAll(); } } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { if (req instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) req; if(!isAjax(httpServletRequest)){ httpServletRequest.getSession().setAttribute(LAST_ACCESSED, System.currentTimeMillis()); } } chain.doFilter(req, resp); } public static boolean isAjax(request) { return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")); } public void init(FilterConfig config) throws ServletException { Thread t = new Thread(new Runnable() { @Override public void run() { while (LastAccessFilter.this.backgroundThreadEnabled) { synchronized (SYNC_OBJECT) { try { SYNC_OBJECT.wait(3000); } catch (InterruptedException e) { e.printStackTrace(); } if (LastAccessFilter.this.backgroundThreadEnabled) { HttpSession[] sessions; synchronized (activeSessions){ sessions = activeSessions.toArray(new HttpSession[activeSessions.size()]); } cleanupInactiveSessions(sessions); } } } } private void cleanupInactiveSessions(HttpSession[] sessions) { for (HttpSession session : sessions) { Object lastAccessedObject = session.getAttribute(LAST_ACCESSED); if(lastAccessedObject == null) continue; long lastAccessed = (Long)lastAccessedObject; if(System.currentTimeMillis() > (lastAccessed + 1800000)){//30 Minutes session.invalidate(); } } } }); t.setDaemon(true); this.backgroundThreadEnabled = true; t.start(); } private final List activeSessions = new ArrayList(); @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { synchronized (activeSessions) { this.activeSessions.add(httpSessionEvent.getSession()); } } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { synchronized (activeSessions) { this.activeSessions.remove(httpSessionEvent.getSession()); } } }