Rails:Session工作原理

How Rails Sessions Work
如果你的Rails應用不知道誰在訪問它?如果同一個人請求兩個不同的頁面你不知道怎么處理?如果接到回應后所有保存的數據都消失?

對于大部分靜態站點來說這沒什么大不了的。但是大部分應用需要儲存一些關于用戶的信息。可能是user id,偏好語言或者類似于是否在ipad上呈現應用的桌面版本這樣的設置。

Session是儲存這類信息的完美方案。為多個請求保留的小數據量信息。

Session使用起來很簡單:

session[:current_user_id] = @user.id

但是這其中蘊藏著一些魔法。什么是Session?Rails怎么知道向不同的用戶呈現其對應的信息?以及如何決定存放Session信息的位置?

Session是什么?

Session是一個可以存儲請求信息,使后續請求可以讀取該信息的地方。

可以在一個action中設置數據:

    #app/controllers/sessions_controller.rb
    def create
    # ...
        session[:current_user_id] = @user.id
    # ...
    end

在另一個action中讀取數據:

    #app/controllers/users_controller.rb
    def index
        current_user = User.find_by_id(session[:current_user_id])
    # ...
    end

Session在客戶端瀏覽器和你的Rails應用之間進行了協調,將兩者聯系在了一起。而且是基于Cookie實現的。

當客戶端請求頁面時,服務器會在響應中設置一個Cookie

~ jweiss$ curl -I http://www.google.com | grep Set-Cookie
Set-Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-nVyaoejz-4K6aouUQtyp5B_rK3Z7G-EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly

瀏覽器會存儲Cookie信息。在Cookie過期之前,每次發出請求時,瀏覽器都會將Cookie信息回傳給服務器:

    ...
    > GET / HTTP/1.1
    > User-Agent: curl/7.37.1
    > Host: www.google.com
    > Accept: */*
    > Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-    nVyaoejz-4K6aouUQtyp5B_rK3Z7G-      EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly
    ...

Cookie看起來像一堆亂碼,這是正常的,畢竟Cookie信息并不是給用戶看的。Rails應用可以從Cookie中讀取信息。Rails應用設置了它,當然也能讀取它。

和Session有什么關系?

現在我們有了Cookie。在某次請求中設置了該值,在后續請求中得到同樣的值。那么Cookie和Session的區別是什么呢?
默認情況下,在Rails中兩者并沒有很大的不同。Rails對Cookie做了處理來提高安全性,除此之外,像你預期的一樣:在Rails應用中將數據存入Cookie,也可以將其取出來。從這一點來說,Session和Cookie確實沒有任何區別。
(譯注:這里指在Rails中的用法沒有任何區別,而非概念上沒有任何區別)

但是Cookie不總是存儲Session數據的理想方案:

  • 只能在Cookie中存儲4kb的數據。
    通常情況下夠了,但有時不滿足
  • Cookie在你每次發出的請求中攜帶。
    大的Cookie信息意味著更大數據量的請求(request)響應(reponse),意味著更慢的網站。
  • 如果不小心暴露了secret_key_base,用戶可以修改Cookie中的信息。
    當Cookie中包含類似current_user_id這樣的信息時,每個人都可能進行身份冒充。
  • 在Cookie中保存錯誤類型的數據是不安全的。
    如果小心一點,沒有什么大問題。

當你因為以上一些原因不能選用Cookie來保存Sessio信息時,Rails還提供了其他一些方式來保存Session信息。
(譯注:通常使用cookie來保存session_id)

可選擇的Session存儲方案

其他的Session存儲方案和使用Cookie儲存Session的方式類似。我們來舉一個真實的例子幫助理解。
如果使用ActiveRecord紀錄Session

  1. 當調用session[:current_session_id] = 1時,Session實際上還沒有存在。
  2. Rails會在session表中使用隨機的Session id(例如:09497d46978bf6f32265fefb5cc52264)插入一條新紀錄。
  3. 保存{current_user_id: 1}(base64編碼)在該記錄的data屬性中。
  4. 將生成的session id(09497d46978bf6f32265fefb5cc52264)使用set-cookie返回給瀏覽器。

下次發出請求時,

  1. 瀏覽器使用Cookie頭向服務器發送同樣的Cookie信息(Cookie: _my_app_session=09497d46978bf6f32265fefb5cc52264;
    path=/; HttpOnly)
  2. 當調用session[:current_user_id]時
  3. 服務器取出cookie頭中的session id,并在sessions表中找到這條紀錄。
  4. 然后,返回該記錄data屬性的包含的current_user_id信息。

不管你將Session存儲在數據庫,memcached,redis或者其他的地方,幾乎都遵循以上的過程。Cookie只存儲session id,Rails應用使用session id在Session存儲中查找相應的數據。

cookie存儲,緩存存儲還是數據庫存儲?

如果滿足需求的話,將Session信息存儲在Cookie中是最簡單的方法。不需要額外的構造或者設置。
但是如果需要將Session從Cookie存儲中移出的話,你有兩個選項。
存儲在數據庫或存儲在緩存。

在緩存中存儲session信息

你可能已經使用了類似于memcache之類的來緩存你的局部頁面或數據。考慮到緩存相關信息已經配置過了,緩存存儲是存儲Session數據第二簡單的方式

不用擔心Session存儲增長過快,因為當緩存信息過大時,舊的Session信息會被自動清除。而且因為數據存儲在緩存中類似于在內存中,訪問速度是很快的。

但是這也不完美

  • 如果需要保存舊的Session信息,你可能不想讓它們在緩存信息過多時被清除。
  • Session和緩存數據爭奪空間。如果沒有足夠的空間,你可能面臨緩存缺失和Session過早過期的問題。
  • 如果需要重置緩存時(例如升級rails,舊的緩存數據不再可用),只能將所有的Session信息過期。

在數據庫中存儲session信息

如果想存儲Session信息直到它真正失效,應該考慮將其存儲在數據庫中。無論是Redis,AcitiveRecord或者是其他的形勢。

但是數據庫存儲也存在短板:

  • Session不能被自動清除(只能親自清除過期的Session)
  • 需要了解Session數據滿溢時,數據庫如何表現
  • 創建Session是更加小心,否在會在數據庫中填滿無用的Session信息

應該怎樣存儲Session信息?

如果不在意Cookie存儲帶來的限制,那就用它。畢竟它不需要過多配置,并且維護簡單。

存儲在緩存中還是在數據庫中,取決于怎樣看待Session過早失效的問題。我認為Session信息本來就應該是暫時的,所以更傾向于將其存在緩存中。

所以我的選擇順序一般是優先cookie,其他緩存,最后考慮數據庫。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • http協議有http0.9,http1.0,http1.1和http2三個版本,但是現在瀏覽器使用的是htt...
    一現_閱讀 1,899評論 0 3
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 背景在HTTP協議的定義中,采用了一種機制來記錄客戶端和服務器端交互的信息,這種機制被稱為cookie,cooki...
    時芥藍閱讀 2,383評論 1 17
  • 1. cookie 1.1 什么是cookie cookie 是存儲于訪問者的計算機中的變量。每當同一臺計算機通過...
    cbw100閱讀 4,095評論 0 13
  • 煙抽了一半就主動滅掉 一顆蘋果洗干凈又懶得咬下第一口 牙齦會出血吧 他這樣想道 一個人做飯 從不系圍裙 連多加一個...
    段童閱讀 202評論 0 0