【问题标题】:JSF 2.0 Session Timeout throws IllegalStateExceptionJSF 2.0 会话超时抛出 IllegalStateException
【发布时间】:2025-12-18 21:20:01
【问题描述】:

我必须在会话超时后注销用户。我使用EventListenter 清除会话,还使用PhaseListener 在执行任何处理之前验证用户。

当我在会话超时后尝试单击任何按钮时出现以下异常:

java.lang.IllegalStateException
org.apache.catalina.connector.ResponseFacade.reset(ResponseFacade.java:310)
com.sun.faces.context.ExternalContextImpl.responseReset(ExternalContextImpl.java:821)
com.sun.faces.context.ExceptionHandlerImpl.throwIt(ExceptionHandlerImpl.java:251)
com.sun.faces.context.ExceptionHandlerImpl.handle(ExceptionHandlerImpl.java:141)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:119)
com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:113)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:395)

我的 web.xml 有以下条目:

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/content/country/default/jsp/login.faces</location>
</error-page>
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/content/country/default/jsp/error.faces</location>
</error-page>

PhaseListener 中的一段代码是

try{
    log.info("------------Check the method call-----------");
    if (log.isDebugEnabled())
        log.info("Authentication is needed.  Navigating to login page.");
    nh.handleNavigation(fc, null, NavigationConstants.LOGIN_FAILURE.toString());
    return;
}catch(ViewExpiredException ve){
    log.info("Got the view expired exception. . . ");
    FacesContext.getCurrentInstance().getExternalContext().redirect("login.faces");
}catch(IllegalStateException e){
    log.info("Got the IllegalStateException . . ");
    FacesContext.getCurrentInstance().getExternalContext().redirect("login.faces");
}catch(Exception ex){
    log.info("Got the Exception . . ",ex);
    FacesContext.getCurrentInstance().getExternalContext().redirect("login.faces");

【问题讨论】:

    标签: jsf


    【解决方案1】:
    java.lang.IllegalStateException
    org.apache.catalina.connector.ResponseFacade.reset(ResponseFacade.java:310)
    

    您收到此异常because 响应已提交。

    com.sun.faces.context.ExternalContextImpl.responseReset(ExternalContextImpl.java:821)
    com.sun.faces.context.ExceptionHandlerImpl.throwIt(ExceptionHandlerImpl.java:251)
    com.sun.faces.context.ExceptionHandlerImpl.handle(ExceptionHandlerImpl.java:141)
    com.sun.faces.lifecycle.Phase.doPhase(Phase.java:119)
    com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:113)
    

    所以,JSF 在恢复视图阶段遇到了一个异常,需要抛出它(因此它最终会出现在错误页面中),但是它无法做到,因为响应已经被在那一点上提交(没有通知 JSF 它应该继续立即呈现响应)。

    目前尚不清楚该阶段侦听器究竟何时完成其工作,但您的主要兴趣是确定在恢复视图阶段究竟抛出了哪个异常,以及为什么此时已经提交了响应。根据迄今为止发布的代码和信息无法回答此问题。运行调试器应该会告诉您更多关于异常的信息,并且跟踪您提交响应的代码流应该会告诉您更多关于意外提交的信息。

    不管怎样,你的以下功能需求让我很感兴趣:

    我必须在会话超时后注销用户。我正在使用 EventListenter 来清除会话

    我不明白为什么这是必要的。登录用户通常表示为会话属性。如果会话超时,则登录用户应该已经自动消失。您不需要自己执行任何清理操作。当您需要基于它执行任何操作时,您只需检查会话中登录用户的存在

    【讨论】:

    • 基本上应用程序会创建一个身份验证令牌并将其记录在数据库中。要求是在会话到期或用户注销后失效。所以我试图在 sessionDestroy 方法中使身份验证令牌无效。让我运行调试器,如果我对此有所了解,会回复您。谢谢
    • sessionDestroyed() 实现的HttpSessionListener 方法,你的意思是?我仍然想知道那个事件监听器和阶段监听器与它有什么关系。该阶段侦听器究竟何时运行?
    • 是的,phaseListener 在 afterPhase(PhaseEvent event) 方法中,我们在其中检查用户身份验证。我不确定那段代码是否抛出异常。同样的代码也适用于我们直接调用的一些页面,例如 /abc.faces,但它不适用于 基本上对于调用 bean 特定方法的一些点击。
    • 是的,但是在哪个阶段?至于回发期间的异常,那只能意味着它是ViewExpiredException,这是非常正常的,因为会话(以及视图状态)已经过期。但你真正的问题是响应已经在那个时候提交了。它不应该在那个时候提交,或者至少应该告诉 JSF 不要执行生命周期。您在浏览器中看到什么样的页面?您是否在某处致电response.sendRedirect() 而不会阻止其余的请求/响应?也许在某些Filter
    • 我不认为我在做 response.sendRedirect,我在会话中时在一个页面上。然后会话到期,我单击一个按钮,该按钮进入特定 bean 的操作。那个时候它进入 phaseListener 并抛出异常。