2012-05-30 78 views
3

我們的Web應用程序使用JBoss 7.1.1和Java(JPA2和RichFaces4)運行。目前的問題是,如果用戶登錄但應用程序內沒有執行任何操作一段時間(可能是由於會話超時引起的),則應用程序會卡住。然後,用戶必須再次加載Web應用程序,這看起來不太專業。如何在Web應用程序中實現自動註銷?

你能給我一個提示,說明如何使用上述技術實現自動註銷嗎?

[UPDATE]
我嘗試了很多的可能性,但沒有一個能正常工作。我的想法是實現一個SessionTimeoutListener,它知道會話何時過期:

@Logged 
@WebListener 
@Named 
public class SessionTimeoutListener implements HttpSessionListener 
{ 
    @Inject 
    private Logger logger; 

    @Override 
    public void sessionCreated(HttpSessionEvent event) 
    { 
     // not used. 
    } 

    @Override 
    public void sessionDestroyed(HttpSessionEvent event) 
    { 
     logger.info("Session destroyed."); 

     ApplicationContext.setAutoLoggedOut(true); 
    } 
} 

This works。但是,然後出現所有問題:我無法重定向,因爲FacesContext在那裏爲空。我無法觸發push events,因爲我得到了一些例外,如NameNotFoundException或類似的(我嘗試了很多,但似乎射擊事件也無法解決這個問題)。然後,我嘗試了ApplicationContext.isAutoLoggedOut()上的a4j:poll,但這也不起作用,因爲如果我執行輪詢事件,會話永遠不會過期。我總是走到死衚衕。如果我可以從SessionTimeoutListener以某種方式重定向,這將是解決方案。

[可能的解決方案]
我現在很滿意,當我在視圖中的任何按鈕單擊會話過期之後執行註銷。目前這種解決方案只是基本的,並且不適用於生產,但是這個解決方案很有效,我將在此基礎上構建: 我使用上層的SessionTimeoutListener。此外,我使用了SessionTimeoutListener之後調用的PhaseListener,因此如果會話過期,SessionTimeoutListener將被調用並銷燬會話,但SessionTimeoutPhaseListener仍具有FacesContext。所以,我也可以把那裏註銷頁:

public class SessionTimeoutPhaseListener implements PhaseListener 
{ 
    private static final long serialVersionUID = -8603272654541248512L; 

    @Override 
    public void beforePhase(PhaseEvent event) 
    { 
     //not used. 
    } 

    @Override 
    public void afterPhase(PhaseEvent event) 
    { 
     FacesContext facesContext = event.getFacesContext(); 

     if (ApplicationContext.isAutoLoggedOut()) 
     { 
      ApplicationContext.setAutoLoggedOut(false); 

      try 
      { 
       facesContext.getExternalContext().redirect("./logout.xhtml"); 
      } 
      catch (IOException e) 
      { 
      } 
     } 
    } 

    @Override 
    public PhaseId getPhaseId() 
    { 
     return PhaseId.RESTORE_VIEW; 
    } 
} 

ApplicationContext已與@ApplicationScoped存儲布爾變量的一類,但是這必須要改變,因爲它會影響到誰目前與該應用程序的每個用戶。我想了解一些「ThreadLocal上下文」來解決這個問題。我仍然必須區分自動註銷和手動註銷。偵聽器在兩種情況下都被調用。儘管此解決方案目前工作,但PhaseListener中的重定向也非常棘手,因爲如果我不將「autoLoggedOut」設置爲false,它將被JSF一遍又一遍地調用(導致瀏覽器中出現重定向循環錯誤)。 ..正如我所說,只是基本的,但使用PhaseListener可能是唯一合適的解決方案。

+0

我不明白。會話超時是自動註銷。你的意思是「用戶必須重新加載Web應用程序」。它應該在會話超時後重定向到登錄頁面。 –

回答

1

如果你想處理ViewExpiredException,您可以將以下設置添加到您的web.xml中

<error-page> 
    <exception-type>javax.faces.application.ViewExpiredException</exception-type> 
    <location>/login.jsf</location> 
</error-page> 

更好的是,使用PhaseListener tio檢查用戶在會話中是否仍然可用。

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("User") 

如果不是 - 導航到登錄頁面。

FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, "login"); 
+0

謝謝,但至少有錯誤頁面的概念不適用於我。我想這是因爲我從來沒有見過迄今拋出的ViewExpiredException。順便說一句:我必須把它放在web.xml中,因爲如果我把它放到faces-config中,Eclipse會抱怨這些標籤。 – Bevor

+0

它是Web.xml。感謝您指出。已編輯答案 – kostja

+0

使用PhaseListener是目前唯一的部分工作解決方案。看到我更新的帖子。 – Bevor

3

有兩種方法,你可以這樣做:

1使用拉機制 每5秒左右的時間,不斷地問服務器如果會話是否有效,如果服務器說是有效的,睡的特定時期時間,如果服務器說無效,向用戶顯示消息,並註銷。

2使用推送機制 使用a4j:push,所以在您的服務器必須運行在TimerTask說,每5秒,以檢查是否會話是否有效,如果它認爲會議是有效的,睡眠,如果無效,將消息發送到用戶然後註銷。

貌似RichFaces4在線演示不能正常工作,但檢查了這link

我不知道如果RichFaces已經能像Primefaces Idle Monitor

另一種解決方案是使用Custom HttpSessionListener

1

您必須配置會話超時在web.xml,該值是在幾分鐘內,不能低於1:

<session-config> 
    <!-- The application will have 15 minutes for session timeout --> 
    <session-timeout>15</session-timeout> 
</session-config> 

您應該添加一個過濾器,將檢查會話已過期:

<filter> 
    <filter-name>SessionTimeoutFilter</filter-name> 
    <filter-class>edu.home.TimeoutFilter</filter-class> 
</filter> 

過濾器將是這樣的:

public class TimeoutFilter implements Filter { 
    private String startPage = "index.xhtml"; 

    public void init(FilterConfig filterConfig) throws ServletException {} 
    public void destroy() {} 

    public void doFilter(ServletRequest request, ServletResponse response, 
     FilterChain filterChain) throws IOException, ServletException { 
     if ((request instanceof HttpServletRequest) && 
      (response instanceof HttpServletResponse)) { 
      HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
      HttpServletResponse httpServletResponse = (HttpServletResponse) response; 
      if (!isValidSession(httpServletRequest)) { 
       String timeoutUrl = httpServletRequest.getContextPath() + "/" 
        + startPage; 
       //redirects to the first page 
       httpServletResponse.sendRedirect(timeoutUrl); 
      } 
      filterChain.doFilter(request, response); 
     } 
    } 

    private boolean isValidSession(HttpServletRequest httpServletRequest) { 
     return (httpServletRequest.getRequestedSessionId() != null) && 
       httpServletRequest.isRequestedSessionIdValid(); 
    } 
} 
+0

謝謝,我實現了這一點,但它有時不起作用,因爲在一個案例中我有一個應用程序再次卡住,所以我不能按任何按鈕或鏈接了。奇怪,我想知道爲什麼這已經是第一次了。我嘗試將它與mprabhat的解決方案結合起來,因爲我希望在用戶按任何按鈕之前自動重定向。在會話超時已經導致之後,我可能不應該依賴靜態視圖功能。 – Bevor

1

既然是網頁,你可以使用一些javascript:

var timeout; 
var timeoutTime = 294000; 
$().ready(function() { 
    $(".loading").bind("ajaxSend", function() { 
     timeout = setTimeout("TimedOut()", timeoutTime); 
    } 
}); 

function TimedOut() { 
    //here I do an async call to test if a user is timed out 
    var isTimedOut = yourMethodToTest(); 

    if(isTimedOut) { //do whatever you need to do here; 
     alert("session timed out"); 
    } 
    else { //restart the checker 
     timeout = setTimeout("TimedOut()",timeoutTime); 
    } 
} 
相關問題