How to execute an authenticated AJAX request without resetting the tomcat session timeout?

I have an existing Grails web application that is in production and has a 30 minute session timeout. We run Tomcat (tcServer).

When the user authenticates and on certain pages I want to make several periodic ajax requests to the server that do not extend this 30 minute session timeout, so that the session does not time out.

The question is similar to this asp.net unanswered question , but none of the answers will be made there, and that is in the Java / Tomcat area.

How to execute an authenticated AJAX request without resetting the tomcat session timeout?

Is there some kind of filtering mechanism or url matching that I can use to exclude requests from extending the session timeout?

+8
java ajax tomcat grails session-timeout
source share
2 answers

I would go with a Grails filter that does something similar to what The-MeLLeR offers without an extra loop through all sessions:

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 } } } } 

The session timeout must be incremented or otherwise stored in synchronization with the value in web.xml.

Two problems remain. One of them is the case when there is an Ajax request, but not the previous request is not Ajax (lastAccess == null). Another way is to redirect the browser to the login page or where you need to go when there is an Ajax request after 30 minutes of inactivity without Ajax. You will need to display JSON or some other response that the client will check to see if it has timed out and perform a client-side redirect.

+4
source share

Impossible ...

One of the options:

1) create the javax.servlet.Filter file and save the timestamp of the last (non-ajax) page view in the session.

2) create javax.servlet.http.HttpSessionListener to keep all active sessions.

3) use the background thread to cancel all expired sessions.


Code example:

 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<HttpSession> activeSessions = new ArrayList<HttpSession>(); @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()); } } } 
0
source share

All Articles