2011-01-29 13 views
8

Tengo una aplicación web Grails existente que está en producción y tiene un tiempo de espera de sesión de 30 minutos. Estamos ejecutando Tomcat (tcServer).¿Cómo ejecuto una solicitud autenticada de AJAX sin restablecer el tiempo de espera de sesión de Tomcat?

Cuando un usuario es autenticado y en ciertas páginas quiero realizar algunas consultas periódicas de ajax al servidor que no extienden este tiempo de espera de sesión de 30 minutos, para que no se frustre el tiempo de espera de la sesión.

La pregunta es similar a this unanswered asp.net question, pero ninguna de las respuestas lo hará y esto en el reino de Java/Tomcat.

¿Cómo ejecutar una petición AJAX autenticado sin restablecer tiempo de espera de la sesión del Tomcat?

¿Hay algún tipo de filtro o URL de coincidencia de mecanismo que pueda utilizar para excluir las solicitudes de extender el tiempo de espera de sesión?

+0

puede implementar su propio administrador de sesión (al menos eso es lo que haría) extender org.apache.catalina.session.StandardManager – bestsss

Respuesta

4

me gustaría ir con un filtro Griales que hace algo similar a lo que El-Meller propone sin el bucle innecesaria a través de todas las sesiones:

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

El tiempo de espera de la sesión debe inyectarse por dependencia o mantenerse en sincronización con el valor en web.xml.

Hay dos problemas restantes. Uno es el caso en el que hay una solicitud de Ajax pero no una solicitud anterior que no sea de Ajax (lastAccess == null). La otra es cómo redirigir el navegador a una página de inicio de sesión o donde sea que necesite ir cuando hay una solicitud de Ajax después de 30 minutos sin ninguna actividad que no sea de Ajax. Tendría que representar JSON u otra respuesta que el cliente verificaría para saber que se ha agotado el tiempo de espera y realizar un redireccionamiento del lado del cliente.

+0

. En lugar de codificar la sessionTimeout, ¿no funcionaría 'session.maxInactiveInterval * 1000' también? – Igor

0

Nop no es posible ...

Una opción es la siguiente:

1) crear una javax.servlet.Filter y almacenar la fecha y hora de la última) visita de página (no Ajax en la sesión.

2) crear un javax.servlet.http.HttpSessionListener para almacenar todas las sesiones activas.

3) usar un subproceso de fondo para invalidar todas las sesiones expiradas.


Código de ejemplo:

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

'session.invalidate();' puede lanzar una excepción if (! IsValidInternal()) ... que si ya ha expirado (por ejemplo) ya que el hilo de fondo está trabajando sobre una copia superficial, puede competir efectivamente con el administrador de sesión estándar. La otra parte es que tomcat recicla las sesiones y el hilo de fondo podría estar trabajando en una versión reciclada (¡y puede caducar!). – bestsss

+0

No lo crea ... Cuando la sesión se invalida (o Tomcat la vence), se elimina del mapa de sesiones mediante el método HttpSessionListener .sessionDestroyed. Pero probablemente sea mejor agregar una instrucción try/catch para evitar que el hilo de fondo mate a –

Cuestiones relacionadas