Storing persistent session data in Rails without affecting normal session expiration
I'd like to store some persistent data for each browser (user settings), and don't want to require a login to use the site. What's the bset way to achieve this?
I've considered two approaches:
- Store the info in the session cookie. The problem with this approach is that I would need to set the expiration time on the session cookie, which has the side effect of causing a user's logi开发者_StackOverflow社区n session to not expire when the browser closes.
- Store the info in the DB and store a retrieval key in a cookie on the client-side. I'm concerned about performance issues, as this would require additional queries and possibly some deserialization to retrieve the data. It looks like Rails switched its default from ActiveRecordStore a while back due to performance reasons: https://web.archive.org/web/20120102024844/https://www.ryandaigle.com/articles/2007/2/21/what-s-new-in-edge-rails-cookie-based-sessions
What's the recommended way to achieve this?
Why not just use a non-session cookie? You can specify how long it will persist on the client, etc. See http://api.rubyonrails.org/classes/ActionController/Cookies.html
If you're concerned about the user messing with the cookie data, you can encrypt the cookie data using a technique like this (taken from http://www.neeraj.name/blog/articles/834-how-cookie-stores-session-data-in-rails):
cookie_data = {:foo => "bar"}
digest = 'SHA1'
secret = 'my_encryption_secret'
data = ActiveSupport::Base64.encode64s(Marshal.dump(cookie_data))
digest_value = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), secret, data)
escaped_data_value = Rack::Utils.escape(data)
final_output = "#{escaped_data_value}--#{digest_value}"
cookies[:user_data] = final_output
and later to read the cookie data:
Marshal.load(ActiveSupport::Base64.decode64(cookies[:user_data]))
It was actually changed from a file-based session store to a cookie-based session store - not from ActiveRecord. I may be wrong but I believe ActiveRecord was a viable option for web farms or distributed setups when a file-based store was the default. Since the cookie-based store was introduced, the web farm scenario has become a non-issue because it's stored at the client-side. Today ActiveRecord is still a viable option where you want to store a greater quantity of data than a cookie permits, when you want to cutdown the overhead of data transmitted in each request (using ActiveRecord means you're only transmitting the session_id), or if you want a centralized-session setup.
I find that in employing an ActiveRecord session store, speed is not a factor for me. In a cookie-based session you generally limit what you store in your session variables anyway, so they tend to be tokens used to lookup data in the database. If the data doesn't need to be persisted beyond the session, then it's viable to store the object in the session rather than just a token, because you're going to hit the database for the associated records anyway. If the object you want to retrieve involves an expensive retrieval operation, and you need it during the lifetime of the session, it might make sense to store that object in the session rather than just a token, and only hit the db once when the session is first established.
Cookies are great, but remember also that a user can delete them when they want, encrypted or not. With privacy concerns on the rise, and quite a few plugins available for browsers that conditionally block cookies, and software that cleans up cookies, you might want to weigh up the pros and cons of using cookies vs. a db-backed session.
@joshsz makes a good point about using a non-session cookie for persisting data beyond the session too. Remember a session has a finite lifetime.
精彩评论