3

我们的 Web 应用程序使用 JBoss 7.1.1 和 Java(JPA2 和 RichFaces4)运行。目前的问题是,如果用户登录但在一段时间内没有在应用程序内执行任何操作(可能是由于会话超时引起的),应用程序就会卡住。然后用户必须再次加载Web应用程序,这看起来不太专业。

您能否给我一个提示,如何使用上述技术实现自动注销?

[更新]
我尝试了很多可能性,但没有一个能正常工作。我的想法是实现一个知道会话何时过期的 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 context”来解决这个问题。我仍然必须区分自动注销和手动注销。在这两种情况下都会调用侦听器。虽然这个解决方案目前有效,但 PhaseListener 中的重定向也很棘手,因为如果我不将“autoLoggedOut”设置为 false,JSF 会一遍又一遍地调用它(这会导致浏览器中的重定向循环错误)。 .. 正如我所说,只是初步的,但使用 PhaseListener 可能是唯一合适的解决方案。

4

4 回答 4

3

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

1 使用拉机制 每5秒左右,不断询问服务器会话是否有效,如果服务器说是有效,则休眠指定时间段,如果服务器说无效,则向用户显示消息,然后注销。

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

看起来 RichFaces4 在线演示不起作用,但请查看此链接

我不确定 RichFaces 是否有Primefaces Idle Monitor 之类的东西

另一种解决方案是使用自定义 HttpSessionListener

于 2012-05-30T14:47:12.687 回答
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);
    }
}
于 2012-05-30T17:21:32.033 回答
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");
于 2012-05-30T14:48:09.673 回答
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();
    }
}
于 2012-05-30T15:02:41.143 回答