深入理解session機制
1. session概念
2. http協議與狀態保持
3. 理解cookie
4. php中session的生成機制
5. php中session的過期回收機制
6. php中session的客戶端儲存機制
1. session概念
在web伺服器蓬勃發展的時代,session在web開發語境下的語義是指一類用來在客戶端與伺服器之間保持狀態的解決方案。
2. http協議與狀態保持
http協議本身是無狀態的,客戶端只需要簡單的向伺服器請求下載某些檔案,無論是客戶端還是伺服器都沒有必要紀錄彼此過去的行為,每一次請求之間都是獨立的。
然而人們很快發現如果能夠提供一些按需生成的動態資訊會使web變得更加有用,就像給有線電視加上點播功能一樣。這種需求一方面迫使HTML逐步添加了表單、指令碼、DOM等客戶端行為,另一方面在伺服器端則出現了CGI規範以響應客戶端的動態請求,作為傳輸載體的HTTP協議也添加了檔案上載、cookie這些特性。其中cookie的作用就是為了解決HTTP協議無狀態的缺陷所作出的努力。至於後來出現的session機制則是又一種在客戶端與伺服器之間保持狀態的解決方案。
session機制可能需要藉助於cookie機制來達到儲存標識的目的。所以有必要了解下cookie。
3. 理解cookie
cookie分發是通過擴充套件HTTP協議來實現的,伺服器通過在HTTP的響應頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應的cookie。然而純粹的客戶端指令碼如JavaScript或者VBScript也可以生成cookie。
而cookie 的使用是由瀏覽器按照一定的原則在後臺自動傳送給伺服器的。瀏覽器檢查所有儲存的cookie,如果某個cookie所宣告的作用範圍大於等於將要請求的資源所在的位置,則把該cookie附在請求資源的HTTP請求頭上傳送給伺服器。
cookie的內容主要包括:名字,值,過期時間,路徑和域。
其中域可以指定某一個域比如.google.com,相當於總店招牌,比如寶潔公司,也可以指定一個域下的具體某臺機器比如www.google.com或者froogle.google.com,可以用飄柔來做比。路徑就是跟在域名後面的URL路徑,比如/或者/foo等等,可以用某飄柔專櫃做比。
路徑與域合在一起就構成了cookie的作用範圍。
如果不設定過期時間,則表示這個cookie的生命期為瀏覽器會話期間,只要關閉瀏覽器視窗,cookie就消失了。這種生命期為瀏覽器會話期的 cookie被稱為會話cookie。會話cookie一般不儲存在硬碟上而是儲存在記憶體裡,當然這種行為並不是規範規定的。如果設定了過期時間,瀏覽器就會把cookie儲存到硬碟上,關閉後再次開啟瀏覽器,這些cookie仍然有效直到超過設定的過期時間
儲存在硬碟上的cookie 不可以在不同的瀏覽器間共享,可以在同一瀏覽器的不同程序間共享,比如兩個IE視窗。
這是因為每中瀏覽器儲存cookie的位置不一樣,比如
Chrome下的cookie放在:
C:\Users\sharexie\AppData\Local\Google\Chrome\User Data\Default\Cache
Firefox下的cookie放在:
C:\Users\sharexie\AppData\Roaming\Mozilla\Firefox\Profiles\tq2hit6m.default\cookies.sqlite (倒數第二個檔名是隨機的檔名字)
Ie下的cookie放在:
C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Cookies
(網上都說是在這裡,但是我一直沒找到)
我在這裡也有一個測試,在firefox下用httplook軟體進行嗅探:
1、在本機上第一次開啟必應網站,抓包:
返回的資料如下:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Set-Cookie: _FS=NU=1; domain=.bing.com; path=/
Set-Cookie: _SS=SID=442E36ABF8F5431E8DFF0CAC018437E3; domain=.bing.com; path=/
Set-Cookie: MUID=32B1FE9DB0EB65B52006FD50B1E86565; expires=Sun, 31-Aug-2014 11:35:51 GMT; domain=.bing.com; path=/
Set-Cookie: OrigMUID=32B1FE9DB0EB65B52006FD50B1E86565%2c15deb35b84a74788ae2d9978e3e657b1; expires=Sun, 31-Aug-2014 11:35:51 GMT; domain=.bing.com; path=/
Set-Cookie: SRCHD=D=2454455&MS=2454455&AF=NOFORM; expires=Sun, 31-Aug-2014 11:35:51 GMT; domain=.bing.com; path=/
Set-Cookie: SRCHUID=V=2&GUID=F6DCC04B2CC54139928925763DAEE04A; expires=Sun, 31-Aug-2014 11:35:51 GMT; path=/
Set-Cookie: SRCHUSR=AUTOREDIR=0&GEOVAR=&DOB=20120831; expires=Sun, 31-Aug-2014 11:35:51 GMT; domain=.bing.com; path=/
P3P: CP=”NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND”
Date: Fri, 31 Aug 2012 11:35:50 GMT
Content-Length: 12787
X-Cache-Lookup: MISS from proxy:8080
我們可以看到sessionId為442E36ABF8F5431E8DFF0CAC018437E3,domain為.bing.com; path為/。伺服器為本使用者建立一個session,id為442E36ABF8F5431E8DFF0CAC018437E3作為客服端的cookie中的SID的值。
2、第二次請求必應網站,請求內容如下:
看到請求中帶有sid為442E36ABF8F5431E8DFF0CAC018437E3的cookie。
伺服器返回資料:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
P3P: CP=”NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND”
Date: Fri, 31 Aug 2012 11:41:12 GMT
Content-Length: 12437
X-Cache-Lookup: MISS from proxy:8080
伺服器檢視本tmp目錄中有一個檔案的名字和SID匹配,知道是一個老使用者,沒有新建session,直接返回資料。
當然也有很多304的返回,表示在expires內直接用使用者的快取即可。
4. php中session的生成機制
我們先來分析一下PHP中是怎麼生成一個session的。設計出session的目的是保持每一個使用者的各種狀態來彌補HTTP協議的不足(無狀態)。session是儲存在伺服器的,既然它用於保持每一個使用者的狀態那它利用什麼來區別使用者的呢?這個時候就得藉助cookie了。當我們在程式碼中呼叫session_start();時,PHP會同時往SESSION的存放目錄(預設為/tmp/)和客戶端的cookie目錄各生成一個檔案。session檔名稱像這樣:
格式為sess_{SESSIONID} ,這時session檔案中沒有任何內容,當我們在session_start();添加了這兩行程式碼:
$_SESSION['name'] =’sharexie’;
$_SESSION['webUlr'] = ’www.qq.com’;
這時檔案就有內容了:
name|s:8:”sharexie”;webUlr|s:10:”www.qq.com”;
這時再看看cookie:
可以看到伺服器為我們自動生成了一個cookie,cookie名稱為”PHPSESSID”,cookie內容是一串字元,其實這串字元就是{SESSIONID}。當我們使用session時,PHP就先生成一個唯一的SESSIONID號(如2bd170b3f86523f1b1b60b55ffde0f66),再在我們伺服器的預設目錄下生成一個檔案,檔名為sess_{SESSIONID},同時在當前使用者的客戶端生成一個cookie,內容已經說過了。這樣PHP會為每一個使用者生成一個SESSIONID,也就是說一個使用者一個session檔案。PHP第一次為某個使用者使用session時就向客戶端寫入了cookie,當這個使用者以後訪問時,瀏覽器會帶上這個cookie,PHP在拿到cookie後就讀出裡面的SESSIONID,拿著這個SESSIONID去session目錄下找session檔案。
5. php中session的過期回收機制
我們明白了session的生成及工作原理,發現在session目錄下會有許多session檔案。當然這些檔案一定不是永遠存在的,PHP一定提供了一種過期回收機制。在php.ini中session.gc_maxlifetime為session設定了生存時間(預設為1440s)。如果session檔案的最後更新時間到現在超過了生存時間,這個session檔案就被認為是過期的了。在下一次session回收的時候就會被刪除。那下一次session回收是在什麼時候呢?這和php請求次數有關的。在PHP內部機制中,當php被請求了N次後就會有一次觸發回收機制。到底是請求多少次觸發一次是通過以下兩個引數控制的:
session.gc_probability = 1
session.gc_divisor = 100
這是php.ini的預設設定,意思是每100次PHP請求就有一次回收發生。概率是gc_probability/gc_divisor (這裡我把session.gc_divisor改為1,好像訪問了很多次都沒有觸發回收事件,不知道什麼原因)。我們瞭解了伺服器端的session過期機制,再來看看客戶端的cookie的過期機制。
如果cookie失效了瀏覽器自然傳送不了cookie到伺服器,這時即使伺服器的session檔案存在也沒用,因為PHP不知道要讀取哪個session檔案。我們知道PHP的cookie過期時間是在建立時設定的,那麼PHP在建立session的同時為客戶端建立的cookie的生命週期是多久呢?這個在php.ini中有設定:session.cookie_lifetime 。這個值預設是0,代表瀏覽器一關閉SESSIONID就失效。那就是說我們把session.gc_maxlifetime和session.cookie_lifetime設定成同一個值就可以控制session的失效時間了。
6. php中session的客戶端儲存機制
由於cookie可以被人為的禁止,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞迴伺服器。解決辦法有:
1、URL重寫,就是把session id直接附加在URL路徑的後面,一種是作為URL路徑的附加資訊,表現形式為http://…../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
2、另一種是作為查詢字串附加在URL後面,表現形式為http://…../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
這兩種方式對於使用者來說是沒有區別的,只是伺服器在解析的時候處理的方式不同,採用第一種方式也有利於把session id的資訊和正常程式引數區分開來。
為了在整個互動過程中始終保持狀態,就必須在每個客戶端可能請求的路徑後面都包含這個session id。
3、表單隱藏欄位。就是伺服器會自動修改表單,新增一個隱藏欄位,以便在表單提交時能夠把session id傳遞迴伺服器。比如下面的表單
<form name=”testform” action=”/xxx”>
<input type=”text”>
</form>
在被傳遞給客戶端之前將被改寫成
<form name=”testform” action=”/xxx”>
<input type=”hidden” name=”jsessionid” value=”ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764″>
<input type=”text”>
</form>
實際上這種技術可以簡單的用對action應用URL重寫來代替。