Gestion des sessions expirées dans JSF (rebelote)

J’avais précédemment traité ce thème dans un autre billet, mais je dois avouer que la méthode que j’y avais présenté n’étatit pas vraiment solide ou fiable.

Donc, suite à quelques recherches et expérimentations, j’ai pu mettre en place un autre filtre beaucoup plus solide qui permet d’intercepter les erreurs de sessions expirées dans JSF (qui sont plus qu’abondants avec ce dernier :) ) pour afficher une page personnalisée au lieu de la mooche page d’erreur par défaut du serveur utilisé.

[Suite:]

En gros, voici une version simplifiée du filtre :

import java.io.IOException; 
 
 
 
import javax.servlet.Filter; 
 
import javax.servlet.FilterChain; 
 
import javax.servlet.FilterConfig; 
 
import javax.servlet.ServletException; 
 
import javax.servlet.ServletRequest; 
 
import javax.servlet.ServletResponse; 
 
import javax.servlet.http.HttpServletRequest; 
 
import javax.servlet.http.HttpServletResponse; 
 
 
 
import org.slf4j.Logger; 
 
import org.slf4j.LoggerFactory; 
 
public class TimeoutFilter implements Filter { 
  private static final Logger log = LoggerFactory 
      .getLogger("webapp.timeoutFilter"); 
 
  private static final String TIMOUT_PAGE = "timeout.html"; 
  private static final String LOGIN_PAGE = "login.faces";  
 
  public void init(FilterConfig filterConfig) throws ServletException { 
  } 
 
  public void doFilter(ServletRequest request, ServletResponse response, 
      FilterChain filterChain) throws IOException, ServletException { 
    if ((request instanceof HttpServletRequest) 
        && (response instanceof HttpServletResponse)) { 
      HttpServletRequest hRequest = (HttpServletRequest) request; 
      HttpServletResponse hResponse = (HttpServletResponse) response; 
 
      if (checkResource(hRequest)) { 
        String requestPath = hRequest.getRequestURI(); 
        if (checkSession(hRequest)) { 
            String timeoutUrl = hRequest.getContextPath() + "/" 
                + TIMOUT_PAGE; 
            log.info("Session is invalid! redirecting to timeoutpage : {}", 
                    TIMOUT_PAGE); 
 
            hResponse.sendRedirect(timeoutUrl); 
            return; 
        } 
 
 
    } 
    filterChain.doFilter(request, response); 
  } 
 
  private boolean checkResource(HttpServletRequest request) { 
    String requestPath = request.getRequestURI(); 
    log.debug("reqPath={}", requestPath); 
    return !(requestPath.contains(TIMOUT_PAGE) ||  
        requestPath.contains(LOGIN_PAGE) ||  
      requestPath.equals(hRequest.getContextPath() + "/")); 
 
  } 
 
  private boolean checkSession(HttpServletRequest request) { 
    return request.getRequestedSessionId() != null 
        && !request.isRequestedSessionIdValid(); 
 
  } 
 
  public void destroy() { 
  } 
 
} 

Quelques notes:

  • J’utilise SLF4J pour le logging
  • Je commence par vérifier s’il s’agit bien d’une requête HTTP
  • Si c’est le cas, je vérifies si la ressource demandée (request.getRequestURI()) est une ressource protégée via la méthode checkResource(). En fait, c’est à vous de décider quelle ressource protéger. Dans cet exemple, je suppose que toute page exceptée celle du timeout, du login et la racine est protégée.
  • Je vérifies ensuite l’état de la session en contrôlant que l’identifiant (jsessionid) est non null, et qu’il est valide
  • Si c’est pas bon, alors je redérige vers la page de timeout (via request.sendRedirect)

Toutefois, cette implémentation n’est pas encore parfaite, car il peut arriver des cas où la session est valide sans que l’utilisateur ne soit authentifié.

Pour résoudre ceci, j’ajoute un autre test (après le premier) qui récupère un bean session (un managed bean JSF en fait, mais managé par Spring) pour vérifier qu’il n’est pas null et qu’un champ particulier est renseigné :

if (hRequest.getSession(false) != null) { 
 
  final WebApplicationContext ac = WebApplicationContextUtils 
 
    .getWebApplicationContext(hRequest.getSession(false).getServletContext()); 
 
  if (ac == null) { 
 
    //redériger vers la page du timeout ... 
 
  } else { 
 
    LoginCtrl loginCtrl = (LoginCtrl) ac.getBean("loginCtrl"); 
 
    if (loginCtrl == null || !loginCtrl.isLoggedIn()) { 
 
    if (!(requestPath.contains(LOGIN_PAGE) || requestPath 
 
      .equals(hRequest.getContextPath() + "/"))) { 
 
      //redériger vers la page du timeout ...        
 
    } 
 
  } 
}

Il faut juste bien vérifer de passer false à getSession : ça m’a fait perdre quelques millions de neuronnes pour comprendre ce qui se passait car j’avais ajouté cette innoncente instruction :

 
log.debug(hRequest.getSession(true); 

erf …

Après, il est possible de peaufiner encore la chose, en ajoutant par exemple un test qui emêche un utilisateur déjà connecté de se reconnecter, i.e. d’accèder à la page de login, chose indispensable quand on bosse avec Spring par exemple, où une manipulation pareille ne recharche pas les beans en scope session …

—-

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: