当状态保存方法设置为客户端且用户会话有效时,在集群环境中获取 ViewExpiredException

Posted

技术标签:

【中文标题】当状态保存方法设置为客户端且用户会话有效时,在集群环境中获取 ViewExpiredException【英文标题】:Getting ViewExpiredException in clustered environment while state saving method is set to client and user session is valid 【发布时间】:2016-05-08 06:11:37 【问题描述】:

我有一个使用 Mojarra 2.2.9 的 JSF 应用程序 并部署在集群环境中的 WebSphere 8.5.5.4 上 并且javax.faces.STATE_SAVING_METHOD 设置为client

尽管我所有的应用程序 bean 都是请求范围的,但有时当用户会话有效并且用户在页面上进行发布请求时,他会收到 ViewExpiredException。什么可能导致此问题,我该如何解决? 将javax.faces.STATE_SAVING_METHOD 更改为server 会解决它吗?如果是这样,这样做对内存有什么影响?

另外,这是否与集群环境有关,也许 Websphere 上缺少一些可以解决问题的配置?

【问题讨论】:

排除显而易见的问题,web.xml 中有 <distributable /> 吗? @BalusC,不,我没有 @BalusC,如果我在 websphere 中配置了会话亲和性,我还需要在我的应用程序的 web.xml 中添加这个标签吗? @BalusC 使用客户端状态保存时是否可以获得 ViewExpiredException?我很好奇,因为我一直认为这是不可能的。 @hwibell:默认情况下,在单个服务器上这是不可能的。但在集群中,当视图在一台服务器中序列化并在另一台服务器中反序列化时,可能会抛出它,而无需将应用程序配置为可分发并设置jsf/ClientSideSecretKey(但这会导致“MAC 未验证”错误,而不是VEE,所以这里有点奇怪)。在单个服务器上,如果设置了com.sun.faces.clientStateTimeout,也可以抛出它,但这里不太可能出现这种情况。 【参考方案1】:

如果客户端状态由一台服务器加密并由另一台服务器解密并且服务器不为此使用相同的 AES 密钥,则会发生这种情况。通常,您还应该在服务器日志中看到以下警告:

错误:MAC 未验证

您需要确保在web.xml 中设置了jsf/ClientSideSecretKey 并使用了固定的AES 密钥,否则每个服务器将在启动/重启期间(重新)生成自己的AES 密钥(在加密视图状态期间使用)。

<env-entry>
    <env-entry-name>jsf/ClientSideSecretKey</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>[AES key in Base64 format]</env-entry-value>
</env-entry>

您可以使用此 sn-p 生成 Base64 格式的随机 AES256(32 位)密钥。

KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // Use 128 for 16bit key.
String key = Base64.getEncoder().encodeToString(keyGen.generateKey().getEncoded());
System.out.println(key); // Prints AES key in Base64 format.

如果您收到Java Security: Illegal key size or default parameters? 错误,请按照链接中的说明安装加密扩展,或者生成一个随机的 AES128(16 位)密钥。

获得密钥后,请绝对确保您没有发布/开源您的密钥。

此外,您还需要确保已将 &lt;distributable /&gt; 标记添加到 web.xml,以便 JSF 将执行更积极的会话污染,并且 HTTP 会话(包括视图范围的 bean 本身!)在服务器之间正确同步。

ViewExpiredException 与客户端状态保存的另一个可能原因是您在web.xml 中设置了特定于 Mojarra 的上下文参数com.sun.faces.clientStateTimeout,它表示传入客户端状态被视为过期之前的时间(以秒为单位)。然而,这里不太可能出现这种情况,因为该上下文参数有一个相当不言自明的名称,您只需瞥一眼 web.xml 就会发现它。

另见:

com.sun.faces.ClientStateSavingPassword - recommendations for actual password? javax.faces.application.ViewExpiredException: View could not be restored

【讨论】:

,添加此参数后,我一直收到错误:java.security.InvalidKeyException: Illegal key size 即使我使用此页面生成 AES 128 位 asecuritysite.com/encryption/keygen 页面生成一个 32 位密钥 (AES256)。您可能需要在您的 JRE 中使用加密扩展:***.com/q/6481627 如果这不是一个选项,请选择较短的密钥,例如16 位 (AES128)。使用此答案***.com/a/28277433 底部的代码 sn-p 生成您自己的代码。您只需将256 替换为128 我正在使用 java 6 是否有任何替代 java 8 的 Base64 的? 我使用org.apache.commons.codec.binary.Base64 和下面的代码,它工作正常:KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128); byte[] encodedBytes = Base64.encodeBase64(keyGen.generateKey().getEncoded()); String key = new String(encodedBytes); @guest:不,它允许完全攻击。这就是为什么我的回答说“在获得密钥后,请绝对确保您不会发布/开源您的密钥。”【参考方案2】:

您的 web.xml 中必须有 balusc 提到的可分发标签

【讨论】:

这个标签有什么用,能不能详细点? 这个标签告诉你的应用服务器复制 HTTP 会话,这个指南是针对 JBoss 应用服务器 blog.akquinet.de/2012/06/21/clustering-in-jboss-as7eap-6 我认为同样适用于 Web Sphere。 但据我了解会话复制,当其中一个服务器完成后,负载均衡器将用户重定向到另一台服务器,然后在启用会话复制时复制会话数据,但在我的情况是所有服务器都已启动并运行,并且用户会话有效

以上是关于当状态保存方法设置为客户端且用户会话有效时,在集群环境中获取 ViewExpiredException的主要内容,如果未能解决你的问题,请参考以下文章

四种会话跟踪技术

springCloud-依赖Spring Security使用 JWT实现无状态的分布式会话

JavaWeb学习——session总结

如何跟踪具有访问令牌的用户是否仍具有有效会话?

Web开发中,用到的4种会话跟踪技术

web应用程序状态管理