l24*_*c4l 170 jsf logout jsf-2 viewexpiredexception
我用容器管理的安全性编写了简单的应用程序.问题是当我登录并打开另一个我注销的页面时,然后我回到第一页,我点击任何链接等或刷新页面我得到这个例外.我想这是正常的(或者可能不是:))因为我退出了会话被破坏了.我该怎么做才能将用户重定向到例如index.xhtml或login.xhtml,并使他免于看到错误页面/消息?
换句话说,如何在我退出后自动将其他页面重定向到索引/登录页面?
这里是:
javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restored.
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:212)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at filter.HttpHttpsFilter.doFilter(HttpHttpsFilter.java:66)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
at java.lang.Thread.run(Thread.java:619)
Run Code Online (Sandbox Code Playgroud)
Bal*_*usC 342
在ViewExpiredException每当将被抛出javax.faces.STATE_SAVING_METHOD被设置为server(默认)和终端用户经由发送视图上的HTTP POST请求<h:form>与<h:commandLink>,<h:commandButton>或<f:ajax>,而相关联的视图状态不是在会话中可用了.
视图状态被标识为隐藏输入字段javax.faces.ViewState的值<h:form>.将状态保存方法设置为server,它仅包含引用会话中序列化视图状态的视图状态ID.因此,当会话由于某种原因(在服务器或客户端超时,或者由于某种原因在浏览器中不再维护会话cookie,或者通过HttpSession#invalidate()在服务器中调用,或者由于服务器特定的会话cookie错误而不再维护时)在WildFly中已知,然后序列化视图状态在会话中不再可用,并且最终用户将获得此异常.要了解会话的工作情况,请参阅servlet如何工作?实例化,会话,共享变量和多线程.
JSF将在会话中存储的视图数量也有限制.达到限制时,最近最少使用的视图将过期.另请参见com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews.
将状态保存方法设置为client,javax.faces.ViewState隐藏的输入字段包含整个序列化视图状态,因此最终用户ViewExpiredException在会话到期时不会获得.但是,它仍然可能发生在群集环境中("错误:MAC未验证"是有症状的)和/或在配置的客户端状态上存在特定于实现的超时和/或服务器在重新启动期间重新生成AES密钥时,另请参阅在群集环境中获取ViewExpiredException,同时将状态保存方法设置为客户端,并且用户会话有效如何解决.
无论解决方案如何,请确保您不使用enableRestoreView11Compatibility.它根本不会恢复原始视图状态.它基本上从头开始重新创建视图和所有相关的视图范围的bean,从而丢失所有原始数据(状态).由于应用程序将以令人困惑的方式运行("嘿,我的输入值在哪里...... ??"),这对用户体验非常不利.更好地使用无状态视图,或者<o:enableRestorableView>只能在特定视图上管理它,而不是在所有视图上管理它.
至于为什么 JSF需要视图状态,头部保存到这样的回答:为什么JSF节省了服务器UI组件的状态?
为了避免ViewExpiredException例如在状态保存设置为注销后导航回来时server,仅在注销后重定向POST请求是不够的.您还需要指示浏览器不缓存动态JSF页面,否则浏览器可能会从缓存中显示它们,而不是在您发送GET请求时从服务器请求新的(例如,通过后退按钮).
javax.faces.ViewState缓存页面的隐藏字段可能包含视图状态ID值,该值在当前会话中不再有效.如果您(ab)使用POST(命令链接/按钮)而不是GET(常规链接/按钮)进行页面到页面导航,并单击缓存页面上的这样的命令链接/按钮,那么这将依次失败了ViewExpiredException.
要在JSF 2.0中注销后触发重定向,请添加<redirect />到<navigation-case>问题(如果有),或添加?faces-redirect=true到outcome值.
<h:commandButton value="Logout" action="logout?faces-redirect=true" />
Run Code Online (Sandbox Code Playgroud)
要么
public String logout() {
// ...
return "index?faces-redirect=true";
}
Run Code Online (Sandbox Code Playgroud)
要指示浏览器不缓存动态JSF页面,请创建一个Filter映射到servlet名称的页面,FacesServlet并添加所需的响应标头以禁用浏览器缓存.例如
@WebFilter(servletNames={"Faces Servlet"}) // Must match <servlet-name> of your FacesServlet.
public class NoCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
res.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response);
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
为了避免ViewExpiredException在状态保存设置为时刷新当前页面server,您不仅需要确保通过GET(常规链接/按钮)专门执行页面到页面导航,而且还需要确保您只使用ajax提交表单.如果您同时提交表单(非ajax),那么您最好使视图无状态(请参阅后面的部分),或者在POST后发送重定向(请参阅上一节).
在ViewExpiredException页面刷新是默认配置是非常罕见的情况.只有当JSF将在会话中存储的视图数量限制被命中时,才会发生这种情况.因此,只有当您手动将限制方式设置得太低,或者您在后台不断创建新视图时(例如,通过严格实施的轮询),才会发生这种情况.另请参见com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews.另一个原因是在运行时类路径中有重复的JSF库相互冲突.我们的JSF wiki页面中概述了安装JSF的正确过程.
当你要处理一个无法回避的ViewExpiredException,其已经在某些浏览器标签/窗口打开,当你在另一个标签/窗口注销任意页面上的POST操作后,那么你想指定error-page用于在web.xml肚里到"您的会话超时"页面.例如
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>
Run Code Online (Sandbox Code Playgroud)
如果您打算实际重定向到主页或登录页面,请在错误页面中使用元刷新标头.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session expired</title>
<meta http-equiv="refresh" content="0;url=#{request.contextPath}/login.xhtml" />
</head>
<body>
<h1>Session expired</h1>
<h3>You will be redirected to login page</h3>
<p><a href="#{request.contextPath}/login.xhtml">Click here if redirect didn't work or when you're impatient</a>.</p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
(0in content表示重定向前的秒数,0因此表示"立即重定向",您可以使用例如3让浏览器等待3秒重定向)
请注意,在ajax请求期间处理异常需要特殊的ExceptionHandler.另请参阅JSF/PrimeFaces ajax请求的会话超时和ViewExpiredException处理.您可以在OmniFaces FullAjaxExceptionHandler展示页面上找到一个实时示例(这也包括非ajax请求).
另外请注意,您的"一般"的错误页面应该被映射<error-code>的500,而不是一个<exception-type>如的java.lang.Exception或java.lang.Throwable,否则包裹在所有异常ServletException,如ViewExpiredException在一般的错误页面仍然会结束.另请参见web.xml中java.lang.Throwable错误页面中显示的ViewExpiredException.
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>
Run Code Online (Sandbox Code Playgroud)
一种完全不同的替代方法是在无状态模式下运行JSF视图.这样就不会保存任何JSF状态,并且视图永远不会过期,而只是在每次请求时从头开始重建.您可以通过将transient属性设置<f:view>为true:来打开无状态视图:
<f:view transient="true">
</f:view>
Run Code Online (Sandbox Code Playgroud)
这样javax.faces.ViewState隐藏字段将"stateless"在Mojarra中获得固定值(此时尚未检查MyFaces).请注意,此功能是在Mojarra 2.1.19和2.2.0中引入的,在旧版本中不可用.
结果是你不能再使用视图范围的bean了.它们现在的行为类似于请求范围的bean.缺点之一是您必须通过摆弄隐藏的输入和/或松散的请求参数来自己跟踪状态.主要与输入字段那些形式rendered,readonly或者disabled其通过AJAX事件控制属性将受到影响.
注意,在<f:view>整个视图中不一定需要是唯一的和/或仅驻留在主模板中.重新声明并将其嵌套在模板客户端中也是完全合法的.它基本上"扩展"了父母<f:view>.例如在主模板中:
<f:view contentType="text/html">
<ui:insert name="content" />
</f:view>
Run Code Online (Sandbox Code Playgroud)
并在模板客户端:
<ui:define name="content">
<f:view transient="true">
<h:form>...</h:form>
</f:view>
</f:view>
Run Code Online (Sandbox Code Playgroud)
你甚至可以包装<f:view>在一个<c:if>使它有条件的.请注意,它将应用于整个视图,而不仅仅应用于嵌套内容,例如<h:form>上面的示例.
与具体问题无关,使用HTTP POST进行纯页面到页面导航不是非常用户/ SEO友好.在JSF 2.0,你应该真的喜欢<h:link>或<h:button>在<h:commandXxx>那些为普通的香草页面到页面的导航.
所以不是例如
<h:form id="menu">
<h:commandLink value="Foo" action="foo?faces-redirect=true" />
<h:commandLink value="Bar" action="bar?faces-redirect=true" />
<h:commandLink value="Baz" action="baz?faces-redirect=true" />
</h:form>
Run Code Online (Sandbox Code Playgroud)
更好
<h:link value="Foo" outcome="foo" />
<h:link value="Bar" outcome="bar" />
<h:link value="Baz" outcome="baz" />
Run Code Online (Sandbox Code Playgroud)
Mik*_* GH 55
您是否尝试在下方添加行web.xml?
<context-param>
<param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
<param-value>true</param-value>
</context-param>
Run Code Online (Sandbox Code Playgroud)
当我遇到这个问题时,我发现这非常有效.
首先,在更改web.xml之前,您需要做的是确保您的ManagedBean implements Serializable:
@ManagedBean
@ViewScoped
public class Login implements Serializable {
}
Run Code Online (Sandbox Code Playgroud)
特别是如果你使用MyFaces
| 归档时间: |
|
| 查看次数: |
243033 次 |
| 最近记录: |