JSF Servlet 异常:应用程序会话在服务器之间跳转时无法查看保存状态

JSF Servlet Exception: No view save state when application session jumps between servers

我有一个 Web 应用程序 运行正在 Liberty 服务器上的 CICS 环境中运行。对于工作负载管理,应用程序会话设置为在 3 个不同的服务器之间切换,这在给定会话期间随机发生。执行此操作时,我目前收到以下错误:

Error 500: javax.servlet.ServletException: /{page_name}.xhtml - No saved view 
state could be found for the view identifier: /{page_name}.xhtml

该错误是随机发生的,但似乎只有在应用程序会话跳转到另一台服务器时才会发生。我通过关闭两台服务器验证了这一点,并发现会话 运行 之后没有任何问题。我的应用程序具有安全性,并且在会话启动时需要登录凭据,我相信这会以某种方式影响页面在跳转服务器时的呈现,就好像凭据没有随会话一起跳转一样。我做了一些研究,并尝试通过将以下上下文参数添加到应用程序 web.xml 来将 state_saving 方法从服务器切换到客户端:

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>        
    <param-value>client</param-value>
</context-param>

不幸的是,问题仍然存在。任何建议将不胜感激。如果需要更多信息或更大的堆栈跟踪,请在下方评论!

编辑:堆叠跟踪:

[2/12/19 15:25:53:672 CST] 00000056 com.ibm.ws.webcontainer.webapp                               
E SRVE0315E: An exception occurred: java.lang.Throwable: 
javax.servlet.ServletException: /pltdisable.xhtml - No saved view state 
could be found for the view identifier: /pltdisable.xhtml
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:5006)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.handleRequest(DynamicVirtualHost.java:314)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:995)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.run(DynamicVirtualHost.java:279)
at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1009)
at com.ibm.cics.wlp.impl.CICSHttpRunnable.run(CICSHttpRunnable.java:244)
at com.ibm.cics.wlp.impl.CICSTaskWrapper.runWork(CICSTaskWrapper.java:762)
at com.ibm.cics.wlp.impl.CICSTaskWrapper.run(CICSTaskWrapper.java:415)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at com.ibm.cics.wlp.threading.CICSThread.run(CICSThread.java:245)
at com.ibm.cics.wlp.threading.CICSPooledThreadFactory.joinAsThreadInternal(CICSPooledThreadFactory.java:409)
at com.ibm.cics.wlp.threading.CICSPooledThreadFactory.joinAsThread(CICSPooledThreadFactory.java:335)
at com.ibm.cics.server.internal.ThreadJoiner.main(ThreadJoiner.java:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at com.ibm.cics.server.Wrapper.call_main(Wrapper.java:893)
at com.ibm.cics.server.Wrapper.callOSGiClass(Wrapper.java:2816)
at com.ibm.cics.server.Wrapper.invokeJvmServerOSGiClass(Wrapper.java:2683)
at com.ibm.cics.server.Wrapper.jvmServerOSGiEntry(Wrapper.java:2612)
at com.ibm.cics.osgi.impl.Controller.runService(Controller.java:1413)
at com.ibm.cics.osgi.impl.Controller.acceptRequest(Controller.java:322)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at com.ibm.cics.router.Router.route(Router.java:1317)
Caused by: javax.servlet.ServletException: /pltdisable.xhtml - No saved view 
state could be found for the view identifier: /pltdisable.xhtml
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:214)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1255)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:743)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:440)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1155)
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:4962)
... 28 more

此 post 讨论了同一问题的一些其他选项,如果您无法使客户端状态保存工作。

此外,如果您默认使用 LTPA 来确保安全,您还应该在 Liberty 服务器之间共享 ltpa.keys 文件。

鉴于您的情况,当请求路由到与其会话开始所在的服务器不同的服务器时,很可能会发生 No saved view state 错误,并且该新服务器无法处理它传递的视图状态。

发生这种情况是因为 MyFaces 默认启用了视图状态加密 - 并且默认情况下,该加密的密钥是在启动时随机生成的。因此,给定一个完全默认的配置,集群中的不同服务器将无法共享加密的视图状态,因为每个服务器都将使用不同的随机密钥进行初始化。为了在服务器之间共享加密的视图状态,这些服务器需要配置为使用相同的密钥。这些可以通过这些网络配置参数进行设置:

<!-- Defines the secret (Base64 encoded) used to initialize the secret key
     for encryption algorithm. The size of it depends on the algorithm used for encryption -->
<context-param>
    <param-name>org.apache.myfaces.SECRET</param-name>
    <param-value>your_secret_key</param-value>
</context-param>

<!-- Define the initialization code (Bas64 encoded) that are used to initialize the secret key used
     on the Message Authentication Code algorithm. The size of it depends on the algorithm used for mac calculation -->
<context-param>
    <param-name>org.apache.myfaces.MAC_SECRET</param-name>
    <param-value>your_mac_secret_key</param-value>
</context-param>

MyFaces wiki 详细介绍了安全配置。默认安全参数设置 - 除了您的密钥 - 足以进行安全部署。