【问题标题】:Handle ViewExireException/ajax and display a Primefaces dialog处理 ViewExireException/ajax 并显示 Primefaces 对话框
【发布时间】:2024-01-21 08:56:01
【问题描述】:

我不会将我的用户重定向或转发到另一个页面。所以当我的SessionExpiredExceptionHandler(扩展ExceptionHandlerWrapper)处理ViewExireException。我希望用户留在同一页面上并显示 PrimeFaces 对话框。用于通知会话已过期并且用户需要再次登录(基于对话框)。我使用 Servlet 3.1 功能来登录/注销用户,并使用 Basic/file 进行身份验证方法将用户映射到不同的系统角色。

现在发生的情况是视图/页面在 2 分钟后刷新,但会话没有失效。这只会在页面刷新的第二次发生,在 4 分钟后。

    <session-config>
        <session-timeout>2</session-timeout>
    </session-config>

编辑: 由元标记刷新:

<meta http-equiv="refresh" content="#{session.maxInactiveInterval}" />

如何在第一次发生异常时使SessionExpiredExceptionHandlerinvalidate 会话对象(Servlet 注销),以及如何在客户端调用 JavaScript (expireDlg.show()) 以显示 PrimeFaces 对话框?

我查看了其他一些线程,但没有找到可行的解决方案。 Session time-out

SessionExpiredExceptionHandler

    @Override
    public void handle() throws FacesException {
    for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
        ExceptionQueuedEvent event = i.next();
        ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
        Throwable t = context.getException();
        if (t instanceof ViewExpiredException) {
        ViewExpiredException vee = (ViewExpiredException) t;
        FacesContext fc = FacesContext.getCurrentInstance();
        Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
        NavigationHandler nav = fc.getApplication().getNavigationHandler();                

        try {
            requestMap.put("currentViewId", vee.getViewId());

            nav.handleNavigation(fc, null, "Home");
            fc.renderResponse();

        } finally {
            i.remove();
        }                                
        }
    }
    // At this point, the queue will not contain any ViewExpiredEvents.
    // Therefore, let the parent handle them.
    getWrapped().handle();
    }

web.xml

<exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/home.xhtml</location>
</error-page>

【问题讨论】:

    标签: jsf-2 exception-handling dialog primefaces viewexpiredexception


    【解决方案1】:

    第一次发生异常时,如何使 SessionExpiredExceptionHandler 使会话对象无效(Servlet 注销)

    会话应该已经失效/过期(otherwise a ViewExpiredException wouldn't be thrown at all),所以我看不出手动使其失效/过期有什么用处。但是对于这种情况,您可以按如下方式使其无效:

    externalContext.invalidateSession();
    

    我如何在客户端调用 JavaScript (expireDlg.show()) 来显示 PrimeFaces 对话框?

    您可以使用 PrimeFaces RequestContext API 以编程方式指示 PrimeFaces 在完成 ajax 响应时执行一些 JS 代码。

    RequestContext.getCurrentInstance().execute("expireDlg.show()");
    

    如果您真的不想导航,请不要忘记从异常处理程序中删除导航处理程序块。

    【讨论】:

    • 我想通了,注释掉导航部分。但目前处理程序根本没有被调用。页面只刷新了,但是本地存储的SessionUser还有效吗?
    • 你的意思是,异常处理程序永远不会被构造/调用?您是否按照博客文章中的说明在faces-config.xml 注册了它的工厂,并在其中找到了这个启动示例?
    • 实际上每个请求都会调用 ExceptionHandler 多次。即使没有例外。
    • 我已经按照你的建议做了,我已经删除了导航块,但是我得到了 NullPointerException:*.com/questions/14606828/…
    【解决方案2】:

    此解决方案适用于我的情况。 Primefaces (3.3) 似乎正在吞噬 ExceptionQueuedEvent。当我的 ViewExceptionHandler 被调用时,没有要处理的异常。因此,我将p:idleMonitor 组件与事件侦听器一起使用。我还删除了元刷新标签。

    <p:idleMonitor timeout="#{(session.maxInactiveInterval-60)*1000}">
            <p:ajax event="idle" process="@this" update="sessionMsg" listener="#{userController.userIdleSession()}" />
            <p:ajax event="active" process="@this" update="sessionMsg" listener="#{userController.userActiveSession()}"/>
    </p:idleMonitor>
    

    一个奇怪的事情是,如果 timeoutweb.xmlsession 超时参数完全相同,则不会调用侦听器。

    Bean 函数

    public void userIdleSession() {
        if (!userIdleMsgVisable) {
            userIdleMsgVisable = true;
            JsfUtil.addWarningMessage(JsfUtil.getResourceMessage("session_expire_title"), JsfUtil.getResourceMessage("session_expire_content"));            
        }
    }
    
    public void userActiveSession() {
            if (!userSessionDlgVisable) {
                userSessionDlgVisable = true;                     
                RequestContext.getCurrentInstance().execute("sessionExipreDlg.show()");            
            }
        }
    

    对话框 (sessionExipreDlg) 调用重定向而不是使用导航处理程序来获取新范围并刷新页面。

    public void userInactiveRedirect() {
            FacesContext fc = FacesContext.getCurrentInstance();
            userIdleMsgVisable = false;
            userSessionDlgVisable = false;
            sessionUser = null;         
            HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
            JsfUtil.findBean("homeController", HomeController.class).clearCurrentValues();        
            try {
                fc.getExternalContext().redirect(JsfUtil.getApplicationPath(request, false, null));            
            } catch (IOException ex) {
                BeanUtil.severe(ex.getLocalizedMessage());
            }
        }
    

    【讨论】:

    • 如果用户没有点击页面上的任何地方只是移动鼠标(例如滚动)直到会话到期怎么办。例如,在它点击一个按钮之后。然后会抛出 ViewExpiredException。