PHP Session
本文由以下兩篇博客整理而成, 傳送門如下:
Cookie和Session的作用,區別和各自的應用范圍
PHP session有效期session.gc_maxlifetime
謝謝大神的啟發
概覽
一、cookie機制和session機制的區別
cookie機制采用的是在客戶端保持狀態的方案,而session機制采用的是在服務器端保持狀態的方案。
同時我們也看到,由于在服務器端保持狀態的方案在客戶端也需要保存一個標識,所以session機制可能需要借助于cookie機制來達到保存標識的目的,但實際上還有其他選擇。
二、會話cookie和持久cookie的區別
如果不設置過期時間,則表示這個cookie生命周期為瀏覽器會話期間,只要關閉瀏覽器窗口,cookie就消失了。這種生命期為瀏覽會話期的cookie被稱為會話cookie。會話cookie一般不保存在硬盤上而是保存在內存里。
如果設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器,這些cookie依然有效直到超過設定的過期時間。
三、cookie的發送
1.創建Cookie對象
2.設置最大時效
3.將Cookie放入到HTTP響應報頭
如果你創建了一個cookie,并將他發送到瀏覽器,默認情況下它是一個會話級別的cookie:存儲在瀏覽器的內存中,用戶退出瀏覽器之后被刪除。如果你希望瀏覽器將該cookie存儲在磁盤上,則需要使用maxAge,并給出一個以秒為單位的時間。將最大時效設為0則是命令瀏覽器刪除該 cookie。
Session概述
一、session在不同環境下的不同含義
session,中文經常翻譯為會話,其本來的含義是指有始有終的一系列動作/消息,比如打電話是從拿起電話撥號到掛斷電話這中間的一系列過程可以稱之為一個session。
然而當session一詞與網絡協議相關聯時,它又往往隱含了“面向連接”和/或“保持狀態”這樣兩個含義。
session在Web開發環境下的語義又有了新的擴展,它的含義是指一類用來在客戶端與服務器端之間保持狀態的解決方案。有時候Session也用來指這種解決方案的存儲結構。
二、session的機制
session機制是一種服務器端的機制,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息。
但程序需要為某個客戶端的請求創建一個session的時候,服務器首先檢查這個客戶端的請求里是否包含了一個session標識-稱為session id,如果已經包含一個session id則說明以前已經為此客戶創建過session,服務器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個,這種情況可能出現在服務端已經刪除了該用戶對應的session對象,但用戶人為地在請求的URL后面附加上一個JSESSION的參數)。
如果客戶請求不包含session id,則為此客戶創建一個session并且生成一個與此session相關聯的session id,這個session id將在本次響應中返回給客戶端保存。
PHP中, 如果一個請求中通過session_start()開啟session, 那么在請求結束后, 會給瀏覽器設置一個名稱為session_id的cookie值, 下次請求后, 會自動攜帶該cookie;
三、保存session id的幾種方式
A.保存session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識發送給服務器。
B.由于cookie可以被人為的禁止,必須有其它的機制以便在cookie被禁止時仍然能夠把session id傳遞回服務器,經常采用的一種技術叫做URL重寫,就是把session id附加在URL路徑的后面,附加的方式也有兩種,一種是作為URL路徑的附加信息,另一種是作為查詢字符串附加在URL后面。網絡在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑后面都包含這個session id。
C.另一種技術叫做表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務器。
四、session什么時候被創建
調用session_start()時session被創建
五、session何時被刪除
A.程序調用session_destory()
B.距離上一次收到客戶端發送的session id時間間隔超過了session的最大有效時間
C.服務器進程被停止
六、是否只要關閉瀏覽器,session就消失了
瀏覽器從來不會主動在關閉之前通知服務器它將要被關閉,因此服務器根本不會有機會知道瀏覽器已經關閉。服務器會一直保留這個會話對象直到它處于非活動狀態超過設定的間隔為止。
之所以會有這種錯誤的認識,是因為大部分session機制都使用會話cookie來保存session id,而關閉瀏覽器后這個session id就消失了,再次連接到服務器時也就無法找到原來的session。
如果服務器設置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發出的HTTP請求報頭,把原來的session id發送到服務器,則再次打開瀏覽器仍然能夠找到原來的session。
恰恰是由于關閉瀏覽器不會導致session被刪除,迫使服務器為session設置了一個失效時間,當距離客戶上一次使用session的時間超過了這個失效時間時,服務器就可以認為客戶端已經停止了活動,才會把session刪除以節省存儲空間。
由此我們可以得出如下結論:
關閉瀏覽器,只會是瀏覽器端內存里的session cookie消失,但不會使保存在服務器端的session對象消失,同樣也不會使已經保存到硬盤上的持久化cookie消失。
七、打開兩個瀏覽器窗口訪問應用程序會使用同一個session還是不同的session
通常session cookie是不能跨窗口使用的,當你新開了一個瀏覽器窗口進入相同頁面時,系統會賦予你一個新的session id,這樣我們信息共享的目的就達不到了。
PHP中session的配置
PHP通過session_set_save_handler,接管所有的session管理工作,SESSION可以儲存在文件, 數據庫, redis等當中
1、session.use_cookies:默認的值是“1”,代表SessionID使用Cookie來傳遞,反之就是使用Query_String來傳遞;
2、session.name:這個就是SessionID儲存的變量名稱,可能是Cookie,也可能是Query_String來傳遞,默認值是“PHPSESSID”;
3、session.cookie_lifetime:這個代表SessionID在客戶端Cookie儲存的時間,默認是0,代表瀏覽器一關閉SessionID就作廢……就是因為這個所以Session不能永久使用, 關閉瀏覽器session后, 保存session_id的cookie是會話cookie, 立刻被清除掉,
4、session.gc_maxlifetime:這個是Session數據在服務器端儲存的時間,如果超過這個時間,那么Session數據就自動刪除!
SESSION清除原理
由于PHP的工作機制,它并沒有一個daemon線程,來定時地掃描session信息并判斷其是否失效。當一個有效請求發生時,PHP會根據全局變量session.gc_probability/session.gc_divisor(同樣可以通過php.ini或者ini_set()函數來修改)的值,來決定是否啟動一個GC(Garbage Collector)。
如何讓session永久有效
默認情況下,session.gc_probability = 1,session.gc_divisor =100,也就是說有1%的可能性會啟動GC。GC的工作,就是掃描所有的session信息,用當前時間減去session的最后修改時間(modified date),同session.gc_maxlifetime參數進行比較,如果生存時間已經超過gc_maxlifetime,就把該session刪除。
到此為止,工作一切正常。那為什么會發生gc_maxlifetime無效的情況呢?
在默認情況下,session信息會以文本文件的形式,被保存在系統的臨時文件目錄中。在Linux下,這一路徑通常為\tmp,在 Windows下通常為C:\Windows\Temp。當服務器上有多個PHP應用時,它們會把自己的session文件都保存在同一個目錄中。同樣地,這些PHP應用也會按一定機率啟動GC,掃描所有的session文件。
問題在于,GC在工作時,并不會區分不同站點的session。舉例言之,站點A的gc_maxlifetime設置為2小時,站點B的 gc_maxlifetime設置為默認的24分鐘。當站點B的GC啟動時,它會掃描公用的臨時文件目錄,把所有超過24分鐘的session文件全部刪除掉,而不管它們來自于站點A或B。這樣,站點A的gc_maxlifetime設置就形同虛設了。
找到問題所在,解決起來就很簡單了。修改session.save_path參數,或者使用session_save_path()函數,把保存session的目錄指向一個專用的目錄,gc_maxlifetime參數工作正常了。
嚴格地來說,這算是PHP的一個bug?
還有一個問題就是,gc_maxlifetime只能保證session生存的最短時間,并不能夠保存在超過這一時間之后session信息立即會得到刪除。因為GC是按機率啟動的,可能在某一個長時間內都沒有被啟動,那么大量的session在超過gc_maxlifetime以后仍然會有效。
解決這個問題的一個方法是,把session.gc_probability/session.gc_divisor的機率提高,如果提到100%,就會徹底解決這個問題,但顯然會對性能造成嚴重的影響。另一個方法是自己在代碼中判斷當前session的生存時間,如果超出了 gc_maxlifetime,就清空當前session。
登陸認證案例分析
登陸認證是典型的session應用實例
1 session_start()開啟session
2 瀏覽器請求服務端登陸接口, 服務端判斷用戶名密碼是否正確, 如果正確, 將用戶信息存入$_SESSION中, 服務端響應瀏覽器請求時, 將生成的session_id鍵值作為響應cookie, 通過header set_cookie, 將session_id存入瀏覽器cookie中
3 瀏覽器每次請求該站點, 都會默認攜帶session_id, 服務端根據session_id獲取到當前對應客戶端的session信息, 如果可以獲取到, 說明沒有過期, 還處于登陸狀態,
每一次請求都會刷新session的生命周期
4 如果瀏覽器關閉, cookie中的session_id會被清除掉, 下次請求服務端就獲取不到對應客戶端的session信息, 從而確定客戶端并沒有登陸
5 如果超過session.gc_maxlifetime配置有效期 服務端可能啟動gc, 過期session會被清除掉, 則就算瀏覽器一直沒有關閉, 也會認證失敗
6 每一個客戶端請求過來, 服務端都會生成對應的session以及響應給客戶端唯一的session_id