Hi Steven,
A few things to help your investigations:
As you used to use basic auth, user credentials were sent along with every request, so having a non-sticky load balancer wasn't an issue. However, with form auth, there is a dialog that needs to go on between the client and the server. WIthout a sticky load balancer, each roundtrip of that conversation could occur on a different node, eg: node1 receives original request that decides auth is necessary, does redirect to login page; node 2 serves login page; node 3 receives credentials, does authentication, redirects to the original uri; node 4 receives request for the original uri and checks the authentication. Ideally, the same node would process the whole conversation.
If you have multiple simultaneous requests - even going to the same node - with the NullSessionCache, each one will operate on their own copy of the session. As its a race, one request might see the session fully authenticated, another might not. As you have protected "/*" if you have subordinate urls that you have not specifically excluded (I note you've excluded favico etc) , for eg images, the browser might well issue multiple simultaneous requests.
When using form authentication, the user name and credentials are serialized into the stored session, but not the UserIdentity. This is re-created when the session is deserialized by calling LoginService.login(name, credentials, null), so by the time the SecurityHandler calls FormAuthenticator.validateRequest(request, response, isAuthMandatory) the UserIdentity should be present. So I'd check to make sure that your LoginService is doing the right thing.
Finally, turn on DEBUG for org.eclipse.jetty.security and maybe org.eclipse.jetty.server.session too, and you will see more information about the security client/server dialog that might help you to debug what is going on.