Rails 4 upgrade JSON::ParseError for old sessions

2020-07-02 11:28发布

问题:

After upgrading to Rails 4.1.4 from Rails 3.2, accessing the application with an existing session (from the older Rails 3.2 version) causes an internal server error. backtrace:

JSON::ParserError - 795: unexpected token at {
I"session_id:ETI"%fa78a4ee07ac952c9b034ebc6199f30b;':
  /Users/.../.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/json/common.rb:155:in `parse'
  actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:388:in `load'
  actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:428:in `deserialize'
  actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:183:in `verify_and_upgrade_legacy_signed_message'
  actionpack (4.1.4) lib/action_dispatch/middleware/cookies.rb:550:in `[]'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/cookie_store.rb:114:in `get_cookie'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/cookie_store.rb:90:in `block in unpacked_cookie_data'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/abstract_store.rb:51:in `stale_session_check!'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/cookie_store.rb:89:in `unpacked_cookie_data'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/cookie_store.rb:83:in `block in extract_session_id'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/abstract_store.rb:51:in `stale_session_check!'
  actionpack (4.1.4) lib/action_dispatch/middleware/session/cookie_store.rb:82:in `extract_session_id'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:49:in `block in []'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:48:in `[]'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:70:in `id'
  rack (1.5.2) lib/rack/session/abstract/id.rb:282:in `current_session_id'
  rack (1.5.2) lib/rack/session/abstract/id.rb:288:in `session_exists?'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:152:in `exists?'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:172:in `load_for_read!'
  actionpack (4.1.4) lib/action_dispatch/request/session.rb:89:in `[]'
  warden (1.2.3) lib/warden/session_serializer.rb:30:in `fetch'
  warden (1.2.3) lib/warden/proxy.rb:212:in `user'
  warden (1.2.3) lib/warden/proxy.rb:318:in `_perform_authentication'
  warden (1.2.3) lib/warden/proxy.rb:104:in `authenticate'
  warden (1.2.3) lib/warden/proxy.rb:114:in `authenticate?'
  devise (3.2.4) lib/devise/rails/routes.rb:460:in `block in constraints_for'
  actionpack (4.1.4) lib/action_dispatch/routing/mapper.rb:38:in `block in matches?'
  actionpack (4.1.4) lib/action_dispatch/routing/mapper.rb:36:in `matches?'
  actionpack (4.1.4) lib/action_dispatch/routing/mapper.rb:45:in `call'
  actionpack (4.1.4) lib/action_dispatch/journey/router.rb:71:in `block in call'
  actionpack (4.1.4) lib/action_dispatch/journey/router.rb:59:in `call'
  actionpack (4.1.4) lib/action_dispatch/routing/route_set.rb:678:in `call'
  ...

I tried to change the session cookie key name, but it seems to be stuck on session_id.

# initializers/session_store.rb
MyApp::Application.config.session_store :cookie_store, key: 'myapp_session'

Please help! A great solution would also be to delete all session cookies before they hit the rails middleware, but i have no idea how to do that..

回答1:

Found the answer here: https://github.com/rails/rails/issues/15111

My settings had

# initializers/cookie_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer = :json

I changed it to

Rails.application.config.action_dispatch.cookies_serializer = :hybrid

And that did the trick



回答2:

If you're comfortable with changing your secret key, then it will solve the problem, and I can confirm that people with old cookies will not encounter a 500 error.

Run rake secret to generate a new secret.

If you've implemented config/secrets.yml, put the new secret in there. Otherwise, if you still have your secret in config/initializers/secret_token.rb, put it in there.

Leave your config/initializers/session_store.rb file alone -- you don't need to change it.

In config/initializers/cookie_store.rb, change it to :json:

# Be sure to restart your server when you modify this file.

Rails.application.config.action_dispatch.cookies_serializer = :json

I can confirm that this works, even when your browser is storing an old session cookie. By changing the secret, when someone with an old session cookie visits your site, the server simply ignores the old session state and creates a new session. No 500 error.



回答3:

I just had the same problem and used the answer here and all was fixed. After reading the comments though I found that just changing the secret also fixed the problem, as it should I suppose.

I think changing the secret is a better solution to the problem than switching to :hybrid like @Thibaut Barrère stated in the comments