How do I execute an authenticated AJAX request without resetting the tomcat's session timeout?

前端 未结 2 1627
隐瞒了意图╮
隐瞒了意图╮ 2020-12-17 22:08

I\'ve got an existing Grails Web application that is in production and has a 30 minute session timeout. We are running Tomcat (tcServer).

When a user is authentic

相关标签:
2条回答
  • 2020-12-17 22:38

    I'd go with a Grails filter that does something similar to what The-MeLLeR is proposing without the unnecessary 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 should be dependency-injected or otherwise kept in sync with the value in web.xml.

    There are two remaining issues. One is the case where there's an Ajax request but no previous non-Ajax request (lastAccess == null). The other is how to redirect the browser to a login page or wherever you need to go when there's an Ajax request after 30 minutes of no non-Ajax activity. You'd have to render JSON or some other response that the client would check to know that it's been timed out and do a client-side redirect.

    0 讨论(0)
  • 2020-12-17 22:52

    Nope not possible...

    One option is the following:

    1) create a javax.servlet.Filter and store the timestamp of the last (non-ajax) pageview on the session.

    2) create a javax.servlet.http.HttpSessionListener to store all the active sessions.

    3) use a background thread to invalidate all expired sessions.


    Sample Code:

    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 讨论(0)
提交回复
热议问题