Getting ViewExpiredException in clustered environment while state saving method is set to client and user session is valid

This will happen if the client side state is encrypted by one server and decrypted by other server and the servers don't use the same AES key for this. Normally, you should also have seen below warning in server log:

ERROR: MAC did not verify

You need to ensure that you have set jsf/ClientSideSecretKey in web.xml with a fixed AES key, otherwise each server will (re)generate its own AES key during startup/restart (which is used during encrypting view state).

<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>

You can use this snippet to generate a random AES256 (32bit) key in Base64 format.

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.

In case you get Java Security: Illegal key size or default parameters? error, install the cryptography extension as instructed in the link, or else generate a random AES128 (16bit) key instead.

After having the key, make absolutely sure you don't publish/opensource your key.

Further you also need to ensure you have added <distributable /> tag to web.xml so JSF will perform more agressive session dirtying and the HTTP sessions (including view scoped beans themselves!) are properly synced across servers.

Another probable cause of ViewExpiredException with client side state saving is that you've set the Mojarra-specific context param com.sun.faces.clientStateTimeout in web.xml which represents the time in seconds before an incoming client side state is considered expired. This is however unlikely the case here as that context param has a rather self-explaining name which you would have spotted by just glancing over web.xml.

See also:

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

You must have the distributable tag in your web.xml as mentioned by balusc