我有一个使用Primefaces 6.0 / JSF 2.2的应用程序,其中有以下情形。
用户通过与应用程序中的@SessionScoped bean交互来登录,并被重定向到homepage.jsf。之后,用户通过菜单项(mypage.jsf)导航到页面。
public void login() throws IOException {
//.... Authentication mechanism here
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
externalContext.redirect(new StringBuilder(externalContext.getRequestContextPath())
.append("/homepage.jsf").toString());
return;
}
然后,他从同一选项卡中退出应用程序,并自动重定向到登录页面。
public String logout() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.invalidateSession();
return "/login/login.jsf?faces-redirect=true";
}
注意:虽然登录/注销实现位于@SessionScoped处理程序中,但应用程序的其余操作/重定向位于@ViewScoped。
用户再次登录该应用程序,并尝试浏览之前(mypage.jsf)所在页面的同一p:menu项。
<p:menuitem value="" url="/mypage.jsf?faces-redirect=true" />
这时,应用程序通过javax.faces.application.ViewExpiredException:无法还原视图。在stackoverflow的众多帖子中对此行为进行了描述,其中一个会话在会话(javax.faces.application.ViewExpiredException: View could not be restored)无效之后给出了有关此行为的详细信息
我尝试过的事情: 我知道这无关紧要,并且在JSF 2.2上默认情况下将其设置为false,仍然尝试一下。
<context-param>
<param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
<param-value>false</param-value>
</context-param>
手动设置会话中的视图数。请注意,只有1个或2个用户在该应用程序上工作,但以防万一。
<context-param>
<description></description>
<param-name>com.sun.faces.numberOfViewsInSession</param-name>
<param-value>15</param-value>
</context-param>
<context-param>
<description></description>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>15</param-value>
</context-param>
此外,我已经创建了一个过滤器来避免缓存,如此处Prevent user from seeing previously visited secured page after logout
所述@WebFilter(servletNames = { "facesServlet" })
public class LoginFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP
res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
res.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(request, response);
return;
}
}
我认为将 javax.faces.STATE_SAVING_METHOD 设置为 client 可能会解决我的问题,但这是我要避免的事情。
此外,我创建了一个异常处理程序来处理Viewexpireexception并将用户重定向到特定页面。当然,这只是为了解决这种情况而不是解决问题。
更新1
我试图将ajax设置为false的commandLink进行“ mypage.jsf”上的重定向,以防万一这是一个问题,但具有完全相同的异常
<h:commandLink action="/mypagejsf?faces-redirect=true" ajax="false" value="My Link" />
更新2 首次注销并从登录页面分析请求标头后,我注意到以下内容:
1)浏览器上的sessionID与服务器上的sessionID匹配
2)Referer属性显示到以下URL http://..../mypage.jsf?faces-redirect=true。这是我尝试访问它时收到异常的页面。
更新3 我还注意到,当我尝试第二次登录后(在发生异常之前)访问mypage.jsf时,我的客户端请求(如果我没有记错的话)使用服务器上也存在的相同sessionId。发生异常时,服务器上的会话将被破坏并创建一个新会话。