2012-09-19 59 views
5

我有一個JSF頁面,它不受j_security_check的保護。我執行以下步驟:如果JSF頁面受j_security_check保護,則不會引發Ajax請求ViewExpiredException

  1. 在瀏覽器中打開JSF頁面。
  2. 重新啓動服務器。
  3. 單擊JSF頁面上的命令按鈕以啓動ajax調用。

Firebug顯示ViewExpiredException正如預期的那樣上升。

帖子:

javax.faces.ViewState=8887124636062606698:-1513851009188353364 

響應:

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

但是,一旦我配置頁面,通過j_security_check保護,執行上面列出,奇怪的是相同的步驟(我)ViewExpiredException不再提出。相反,響應只是一種新的視圖狀態。

帖子:

javax.faces.ViewState=-4873187770744721574:8069938124611303615 

響應:

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

有人可以幫我想出解決辦法?我期望它會引發異常,以便我可以處理該異常並顯示錯誤頁面。現在它只是響應一個新的ViewState,我的頁面被卡住,沒有任何視覺反饋。

回答

7

我能夠重現您的問題。這裏發生的事情是容器調用RequestDispatcher#forward()到安全約束中指定的登錄頁面。但是,如果登錄頁面本身也是一個JSF頁面,那麼將在轉發的請求上調用FacesServlet。由於請求是轉發,因此只會在轉發資源(登錄頁面)上創建一個新視圖。但是,由於這是一個ajax請求,並且沒有render信息(整個POST請求在安全檢查期間基本上被丟棄),所以只會返回視圖狀態。

請注意,如果登錄頁面不是JSF頁面(例如JSP或純HTML),那麼ajax請求將返回頁面的整個HTML輸出作爲ajax響應,這是JSF ajax無法解析的,並被解釋爲「空「迴應。

不幸的是,它「按設計」工作。我懷疑在JSF規範中對於Ajax請求的安全約束檢查存在一些疏漏。原因畢竟是可以理解的,並且幸運的是很容易解決。只是,您實際上不希望在此處顯示錯誤頁面,而只是完整地顯示登錄頁面,完全與在非Ajax請求期間發生的情況完全相同。你只需要檢查當前請求是否是ajax請求並且已經被轉發到登錄頁面,那麼你需要發送一個特殊的「重定向」ajax響應,這樣整個視圖就會被改變。

您可以用PhaseListener如下做到這一點:

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

更新這個解決方案是因爲OmniFaces 1.2被內置到OmniPartialViewContext。因此,如果您碰巧已經使用OmniFaces,那麼解決此問題fully transparently,您不需要爲此定製PhaseListener

+0

謝謝BalusC。保存了我的一天。 –

+0

不客氣。 – BalusC

+0

哦另外一個問題,我想知道爲什麼會發生以下情況:如果我使用context.getExternalContext()。redirect(loginURL),它確實將我重定向到登錄頁面。但是登錄後,瀏覽器會顯示一個以ViewState爲內容的XML文件。 xml文件與我在我的問題中發佈的第二個xml文件完全相同。如果我使用context.getExternalContext()。redirect(homepageURL),一切正常。它會帶我登錄頁面。登錄後,主頁將顯示。 –

相關問題