如何在Web应用程序中实现自动注销?

时间:2012-05-30 14:35:50

标签: java jsf jsf-2 richfaces jboss7.x

我们的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在那里是空的。我无法触发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可能是唯一合适的解决方案。

4 个答案:

答案 0 :(得分:3)

有两种方法可以做到这一点:

1使用拉动机制   每隔5秒左右,不断询问服务器会话是否有效,服务器是否有效,睡眠指定时间,服务器是否无效,向用户显示消息,以及注销。

2使用推动机制    使用a4j:push,因此在您的服务器中,您有TimerTask运行说每5秒检查会话是否有效,如果它看到会话有效,则睡眠,如果无效,则向用户发送消息然后退出。

看起来RichFaces4在线演示无效,但请查看此link

我不确定RichFaces是否有类似Primefaces Idle Monitor

的内容

另一种解决方案是使用Custom HttpSessionListener

答案 1 :(得分: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");

答案 2 :(得分: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();
    }
}

答案 3 :(得分: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);
    }
}