2011-01-29 17 views
8

Ho un'applicazione Web Grails esistente in produzione e con un timeout della sessione di 30 minuti. Stiamo eseguendo Tomcat (tcServer).Come si esegue una richiesta AJAX autenticata senza reimpostare il timeout della sessione di tomcat?

Quando un utente è autenticato e su determinate pagine desidero effettuare periodicamente richieste di polling ajax al server che non estendono questo timeout della sessione di 30 minuti, in modo che il timeout della sessione non venga contrastato.

La domanda è simile a this unanswered asp.net question, ma nessuna delle risposte verrà eseguita nel dominio Java/Tomcat.

Come si esegue una richiesta AJAX autenticata senza reimpostare il timeout della sessione di tomcat?

Esiste un tipo di filtro o meccanismo di corrispondenza url che posso utilizzare per escludere richieste dall'estensione del timeout della sessione?

+0

è possibile implementare il proprio gestore di sessione (almeno questo è quello che farei) estendere org.apache.catalina.session.StandardManager – bestsss

risposta

4

mi piacerebbe andare con un filtro Grails che fa qualcosa di simile a quello che il-Meller propone senza il ciclo inutili attraverso tutte le sessioni:

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

Il timeout della sessione deve essere immesso dalla dipendenza o mantenuto in sincronia con il valore in web.xml.

Ci sono ancora due problemi. Uno è il caso in cui vi è una richiesta Ajax ma nessuna precedente richiesta non Ajax (lastAccess == null). L'altro è come reindirizzare il browser a una pagina di accesso o ovunque sia necessario andare quando c'è una richiesta Ajax dopo 30 minuti di attività non Ajax. Dovresti eseguire il rendering di JSON o qualche altra risposta che il client verificherà per sapere che è scaduto e eseguire un reindirizzamento lato client.

+0

Piuttosto che codificare il sessionTimeout, non dovrebbe funzionare anche 'session.maxInactiveInterval * 1000'? – Igor

0

No non è possibile ...

Una possibilità è la seguente:

1) creare una javax.servlet.Filter e memorizzare il timestamp dell'ultimo non ajax) pagina visualizzata nella sessione (.

2) creare un javax.servlet.http.HttpSessionListener per memorizzare tutte le sessioni attive.

3) utilizzare un thread in background per invalidare tutte le sessioni scadute.


codice di esempio:

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();' può generare un'eccezione se (! IsValidInternal()) ... che è se è già scaduto (per esempio) dal momento che il thread in background sta lavorando su una copia superficiale che può efficacemente gareggiare con il gestore di sessioni standard. L'altra parte è che tomcat ricicla le sessioni e il thread in background potrebbe funzionare su una versione riciclata (e può scadere!). – bestsss

+0

Non pensare così ... Quando la sessione viene invalidata (o Tomcat lo espande), viene rimossa dalla mappa delle sessioni dal metodo HttpSessionListener .sessionDestroyed. Ma probabilmente è meglio aggiungere un'istruzione try/catch per evitare che lo sfondothread uccida –

Problemi correlati