2012-09-19 7 views
5

Ho una pagina JSF che non è protetta da j_security_check. Esegui i seguenti passi:ViewExpiredException non lanciato su una richiesta jax se la pagina JSF è protetta da j_security_check

  1. Apre la pagina JSF in un browser.
  2. Riavviare il server.
  3. Fare clic su un pulsante di comando nella pagina JSF per avviare una chiamata Ajax.

Firebug indica che un ViewExpiredException viene generato come previsto.

Messaggio:

javax.faces.ViewState=8887124636062606698:-1513851009188353364 

Risposta:

<partial-response> 
<error> 
<error-name>class javax.faces.application.ViewExpiredException</error-name> 
<error-message>viewId:/viewer.xhtml - View /viewer.xhtml could not be restored.</error-message> 
</error> 
</partial-response> 

Tuttavia, una volta a configurare la pagina per essere protetto da j_security_check ed eseguire la stessa procedura di cui sopra, stranamente (per me) lo ViewExpiredException non viene più generato. Invece, la risposta è solo un nuovo stato di visualizzazione.

Messaggio:

javax.faces.ViewState=-4873187770744721574:8069938124611303615 

Risposta:

<partial-response> 
<changes> 
<update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update> 
</changes> 
</partial-response> 

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.

risposta

7

Sono stato in grado di riprodurre il problema. Quello che sta accadendo qui è che il contenitore invoca un RequestDispatcher#forward() alla pagina di accesso come specificato nel vincolo di sicurezza. Tuttavia, se la pagina di accesso è di per sé una pagina JSF, allora verrà invocato anche il FacesServlet nella richiesta inoltrata. Poiché la richiesta è di tipo forward, verrà semplicemente creata una nuova vista sulla risorsa inoltrata (la pagina di accesso). Tuttavia, poiché si tratta di una richiesta Ajax e non vi sono informazioni 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 accesso non fosse una pagina JSF (ad esempio 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 " vuoto "risposta.

È, purtroppo, funzionante "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.

È possibile raggiungere questo obiettivo 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); 
      } 
     } 
    } 
} 

aggiornamento questa soluzione viene dal OmniFaces 1.2 stato costruito nel OmniPartialViewContext. Quindi, se ti capita di utilizzare già OmniFaces, allora questo problema è fully transparently risolto e non hai bisogno di una personalizzata PhaseListener per questo.

+0

Grazie BalusC. Ho salvato la mia giornata. –

+0

Prego. – BalusC

+0

Oh un'ulteriore domanda, mi chiedo perché succede quanto segue: Se uso context.getExternalContext(). Redirect (loginURL), in effetti mi reindirizza alla pagina di login. Ma dopo l'accesso, un file xml, con ViewState come contenuto, viene visualizzato dal browser. Il file xml è esattamente lo stesso del secondo file xml che ho postato nella mia domanda. Se utilizzo context.getExternalContext(). Redirect (homepageURL), tutto funziona correttamente. Mi richiederà la pagina di accesso. Una volta effettuato il login, verrà visualizzata la home page. –

Problemi correlati