簡體   English   中英

如何在Web應用程序中實現自動注銷?

[英]How to implement an automatic logout in a web application?

我們的Web應用程序使用JBoss 7.1.1和Java(JPA2和RichFaces4)運行。 目前的問題是,如果用戶登錄但應用程序在應用程序內部沒有做任何事情(可能是由於會話超時引起),應用程序就會卡住。 然后,用戶必須再次加載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);
    }
}

這有效。 但隨后出現了所有問題:我無法從中重定向,因為FacesContext在那里是空的。 我無法觸發推送事件,因為我得到一些像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可能是唯一合適的解決方案。

有兩種方法可以做到這一點:

1使用拉機制每隔5秒左右,不斷詢問服務器會話是否有效,服務器是否有效,睡眠指定時間,服務器是否無效,向用戶顯示消息,以及注銷。

2使用push機制使用a4j:push ,所以在你的服務器中你有一個TimerTask運行說每5秒檢查一次會話是否有效,如果它看到會話有效,睡眠,如果無效,發送消息給用戶然后登出。

看起來RichFaces4在線演示不起作用,但請查看此鏈接

我不確定RichFaces是否有像Primefaces Idle Monitor這樣的東西

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

如果要處理ViewExpiredException,可以將以下設置添加到web.xml

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

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

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

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

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

您必須在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();
    }
}

既然它是web,你可以使用一些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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM