ViewExpiredException non lanciata su una richiesta jax se la pagina JSF è protetta da j_security_check

Ho una pagina JSF che non è protetta da j_security_check . Eseguo i seguenti passaggi:

  1. Apri la pagina JSF in un browser.
  2. Riavvia il server.
  3. Fare clic su un pulsante di comando sulla pagina JSF per avviare una chiamata Ajax.

Firebug mostra che viene sollevata una ViewExpiredException , come previsto.

Inviare:

 javax.faces.ViewState=8887124636062606698:-1513851009188353364 

Risposta:

   class javax.faces.application.ViewExpiredException viewId:/viewer.xhtml - View /viewer.xhtml could not be restored.   

Tuttavia, una volta configurata la pagina da proteggere da j_security_check ed eseguito gli stessi passaggi sopra elencati, stranamente (per me) ViewExpiredException non viene più generato. Invece, la risposta è solo un nuovo stato di visualizzazione.

Inviare:

 javax.faces.ViewState=-4873187770744721574:8069938124611303615 

Risposta:

   234065619769382809:-4498953143834600826   

Qualcuno può aiutarmi a capirlo? Mi aspetto che generi un’eccezione in modo da poter elaborare quell’eccezione e mostrare una pagina di errore. Ora risponde solo con un nuovo ViewState, la mia pagina è rimasta bloccata senza alcun feedback visivo.

Sono stato in grado di riprodurre il tuo problema. Quello che sta accadendo qui è che il contenitore richiama un RequestDispatcher#forward() nella pagina di login come specificato nel vincolo di sicurezza. Tuttavia, se la pagina di accesso è di per sé una pagina JSF, allora anche FacesServlet verrà invocato sulla richiesta inoltrata. Poiché la richiesta è di tipo forward, verrà semplicemente creata una nuova vista sulla risorsa inoltrata (la pagina di accesso). Tuttavia, poiché è una richiesta Ajax e non ci sono informazioni di render (l’intera richiesta POST viene sostanzialmente scartata durante il controllo di sicurezza in avanti), verrà restituito solo lo stato di visualizzazione.

Si noti che se la pagina di login non fosse una pagina JSF (es. JSP o HTML semplice), la richiesta ajax avrebbe restituito l’intero output HTML della pagina come risposta ajax che non è analizzabile da JSF ajax e interpretata come risposta “vuota”.

Sfortunatamente funziona “come progettato”. Sospetto che ci sia qualche svista nelle specifiche JSF per quanto riguarda i controlli dei vincoli di sicurezza sulle richieste di ajax. La causa è, dopo tutto, comprensibile e fortunatamente facile da risolvere. Solo, in realtà non vuoi mostrare una pagina di errore qui, ma solo la pagina di login nella sua interezza, esattamente come accadrebbe durante una richiesta non-ajax. Devi solo verificare se la richiesta corrente è una richiesta Ajax ed è stata inoltrata alla pagina di accesso, quindi devi inviare una speciale risposta “ajax” di reindirizzamento in modo che l’intera vista venga modificata.

È ansible ottenere ciò con un PhaseListener come segue:

 public class AjaxLoginListener implements PhaseListener { @Override public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } @Override public void beforePhase(PhaseEvent event) { // NOOP. } @Override public void afterPhase(PhaseEvent event) { FacesContext context = event.getFacesContext(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); String loginURL = request.getContextPath() + "/login.xhtml"; if (context.getPartialViewContext().isAjaxRequest() && originalURL != null && loginURL.equals(request.getRequestURI())) { try { context.getExternalContext().invalidateSession(); context.getExternalContext().redirect(originalURL); } catch (IOException e) { throw new FacesException(e); } } } } 

Aggiorna questa soluzione dal momento che OmniFaces 1.2 è stato incorporato in OmniPartialViewContext . Pertanto, se si utilizza già OmniFaces, questo problema viene risolto completamente in modo trasparente e per questo non è necessario un PhaseListener personalizzato.