PHP Session原理分析及使用

來源:互聯網
上載者:User
之前在一個叫魔法實驗室的部落格中看過一篇《php session原理徹底分析》的文章,作者從session的使用角度很好闡述了在代碼運行過程中,每個環節的變化以及相關參數的設定及作用。本來想把原文轉帖過來,但是原部落格被關閉了。不知是這次大範圍的重新備案,還是其他什麼原因所致。通過百度快照找到一些原文資料,沒找到的將按之前的理解重新整理,以使大家對session能有更多瞭解。

楔子:Session大白話

Session,英文翻譯為“會話”,兩個人聊天,從第一句問好,到最後一句再見,這就構成了一個會話。PHP裡的session主要是指用戶端瀏覽器與服務端資料交換的對話,從瀏覽器開啟到關閉,一個最簡單的會話周期。電腦語言一般怎麼實現會話呢?舉個通俗的例子:
服務端好比一個理髮店,用戶端好比每一個去理髮的客人,很多理髮店都有這種促銷手段,連續消費10次的客人,可以免費一次,大概有三種方式來實現:
1、理髮師傅記性太好,你來過幾次,他看一眼就知道——這叫協議本身支援會話;
2、每個客人發一個會員卡,你每次消費,都要帶著這個卡片,消費一次記錄一筆,當然還要加蓋印章——這叫通過cookie實現會話,缺點是安全性不高,我完全可以偽造會員卡或者公章;
3、理髮店準備一個大帳本,客人每人對應一個會員號或者自己的設定檔,甚至密碼,每個客人來消費,報一下自己的會員號,再把消費次數記錄到大帳本裡——這就是session實現會話,客人腦子裡的會員號就是儲存在用戶端的SESSIONID,大帳本就是儲存在服務端的session資料,這樣相比第二種方法,安全性要高很多,除非你說你把自己的會員號和密碼都搞丟了,這叫做偽造用戶端的SESSIONID。

因為http協議是無狀態的,所以php要實現會話只能通過後面兩種方式,前一種cookie,缺點已經說了,安全性不高,所以重要的會話會選擇使用session。session會話必須依靠一個標識,也可以理解成一個暗號,就是SESSIONID。這是個經過加密的串,儲存在用戶端,通常在cookie裡,用戶端與服務端的每次交流都是通過這個SESSIONID,用戶端先自報家門,伺服器才能找到你在服務端儲存的會話資料,繼續通話。

php.ini常用session設定

[服務端]
session.save_handler = files
預設為file,定義session在服務端的儲存方式,file意為把sesion儲存到一個臨時檔案裡,如果我們想自訂別的方式儲存(比如用資料庫),則需要把該項設定為user;

session.save_path = "/tmp/"
定義服務端儲存session的臨時檔案的位置。

session.auto_start = 0
如置1,則不用在每個檔案裡寫session_start(); session自動start。

session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440
這三個配置組合構建服務端session的記憶體回收機制session.gc_probability與session.gc_divisor構成執行session清理的機率,理論上的解釋為服務端定期有一定的機率調用gc函數來對session進行清理,清理的機率為:gc_probability/gc_divisor 比如:1/100 表示每一個新會話初始化時,有1%的機率會啟動記憶體回收程式,清理的標準為session.gc_maxlifetime定義的時間。

[用戶端]
session.use_cookies = 1
sessionid在用戶端採用的儲存方式,置1代表使用cookie記錄用戶端的sessionid,同時,$_COOKIE變數裡才會有$_COOKIE[‘PHPSESSIONID’]這個元素存在;

session.use_only_cookies = 1
也是定義sessionid在用戶端採用的儲存方式,置1代表僅僅使用 cookie 來存放會話 ID。一般來說,現在用戶端都會支援cookie,所以建議設定成1,這樣可以防止有關通過 URL 傳遞會話 ID 的攻擊。

session.use_trans_sid = 0
相對應於上面那個設定,這裡如果置1,則代表允許sessionid通過url參數傳遞,同理,建議設定成0;

session.referer_check =
這個設定在session.use_trans_sid = 1的時候才會生效,目的是檢查HTTP頭中的"Referer"以判斷包含於URL中的會話id是否有效,HTTP_REFERER必須包含這個參數指定的字串,否則URL中的會話id將被視為無效。所以一般預設為空白,即不檢查。

session.name = PHPSESSID
定義sessionid的名稱,即變數名,通過瀏覽器http工具可以查看PHPSESSID的值;

session.hash_function = 0
選擇session_name的加密方式,0代表md5加密,1代表sha1加密,預設是0,但是據說用sha1方式加密,安全性更高;

session.hash_bits_per_character = 4
指定在session_name字串中的每個字元內儲存多少位位元,這些位元是hash函數的運算結果。
4 bits: 0-9, a-f
5 bits: 0-9, a-v
6 bits: 0-9, a-z, A-Z, "-", ","

url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fieldset="
指定重寫哪些HTML標籤來包含sid(session_id)(僅在"session.use_trans_sid"開啟的情況下有效),URL重寫器將添加一個隱藏的"<input>",它包含了本應當額外追加到URL上的資訊。

session.cookie_lifetime = 0
儲存sessionid的cookie檔案的生命週期,如置0,代表會話結束,則sessionid就自動消失,常見的強行關閉瀏覽器,就會丟失上一次的sessionid;

session.cookie_path = /
儲存sessionid的cookie檔案在用戶端的位置;

session.cookie_domain = /
儲存sessionid的cookie的網域名稱設定,這跟cookie允許的網域名的存取權限設定有關,一般來說想讓自己網站所有的目錄都能訪問到用戶端的cookie,就應該設定成“/”如需要詳細瞭解,可以看下setcookie()函數的domain參數相關設定和使用方法;

session.bug_compat_42 = 1
session.bug_compat_warn = 1
這兩個可以說幾乎是快要被廢棄的設定,是為了老版本的php服務的,主要是針對session_register函數,因為php5的register_global預設是關閉狀態,所以在php5雷根本用不到session_register這個函數;並且php6就要廢除這個設定,直接定義為關閉,所以沒必要研究這兩個了;


session_start()做了些什嗎?

假設php.ini中session的幾個關鍵參數配置為:
session.save_handler = files
session.use_cookies = 1
session.name = PHPSESSID
session.save_path = "/tmp/"

下面通過代碼範例闡述,在一個會話過程中session_start的作用。

程式1:
<?php
session_start();
$_SESSION['uname'] = 'monkey';
$_SESSION['ukey'] = 20119999;
?>


程式1執行後,session_start()會做兩件事:


1、在用戶端產生一個存放PHPSESSID的cookie檔案,這個檔案的存放位置和存放方式跟程式的執行方式有關,不同的瀏覽器也不盡相同,這一步會產生一個序列化後的字串——PHPSESSID;查看瀏覽器中的cookie資訊,可以安裝相關外掛程式。firefox中httpfox,web developer等都是很好的工具。


2、在服務端產生一個存放session資料的臨時檔案,存放的位置由session.save_path參數指定,名稱類似於“sess_85891d6a81ab13965d349bde29b2306c”,“sess_”代表這是個session檔案,“85891d6a81ab13965d349bde29b2306c”即此次會話的PHPSESSID,跟用戶端的PHPSESSID值是一樣的。
用編輯器開啟“sess_85891d6a81ab13965d349bde29b2306c”檔案,會看到一串“uname|s:6:"monkey";ukey|i:20119999;”這樣的內容。這個檔案裡存放的就是$_SESSION變數的具體內容,每個變數用“;"分號隔開。


格式為:變數名 | 變數類型 : [長度] : 值; 例如: uname|s:6:"monkey"; 表示SESSION變數uname的類型為字串,值長度為6,值為monkey.

那麼問題來了,上面說的兩件事,是在程式執行到session_start(),就完成的嗎?這兩件事,誰先誰後呢?
讓實驗來證明,稍微改動一下程式:

程式2:
<?php
session_start();
$_SESSION['uname'] = 'monkey';
$_SESSION['ukey'] = 20119999;

sleep(30);
?>

先把用戶端和服務端的session資料通通刪除,然後執行程式2,趁著程式裡的sleep30秒的工夫,去查看用戶端和服務端的session情況,發現:在程式執行過程中,用戶端並沒有建立儲存PHPSESSID的cookie檔案,服務端卻已經有了儲存session內容的臨時檔案,但是檔案裡沒有內容,等30秒時間過了之後,用戶端的cookie檔案才會產生,服務端的session檔案裡才有了內容。


由此推斷大致流程應該為:在程式執行到session_start()的時候,服務端首先產生PHPSESSID,並產生相對應的session檔案,但是在程式進行$_SESSION賦值的時候,並沒有把相應的值寫入到session檔案裡,姑且臆斷為儲存在記憶體裡吧,到了程式執行完畢後,才會在用戶端產生儲存PHPSESSID的cookie檔案,並把$_SESSION變數裡的值寫入服務端的session檔案裡,至於最後兩個步驟誰先誰後,暫時還沒有想到好辦法來證明。


為了更進一步論證,刪除用戶端和服務端的session相關內容執行程式3,觀察第一次和第二次的結果:
程式3:
<?php
session_start();
$_SESSION['uname'] = 'monkey';
$session_id = session_id();
$sess_file = "/tmp/sess_".$session_id;
$content = file_get_contents($sess_file);

echo '***'.$_COOKIE['PHPSESSID'] .'***';
echo '<br />' . $_SESSION['uname'] . '<br />';
echo '***'.$content.'***';
?>


上面說的是第一次sessin_start()的執行方式,也就是一套程式裡,第一個session_start()出現的時候所做的事情,下面來看之後的session_start():

假設的php.ini配置:session.cookie_lifetime = 0

程式4:
<?php
session_start();
echo $_SESSION['uname'];
echo $_SESSION['ukey'];
?>

現在,用戶端已經有了儲存PHPSESSID的cookie檔案,服務端也有了儲存session內容的sess_檔案,執行程式4,會列印出正常的內容。這時,如果強行關閉瀏覽器,再執行程式4,結果會怎樣呢?

首先,session.cookie_lifetime設定成0,表示用戶端儲存PHPSESSID的cookie檔案的生存周期為0,瀏覽器如果處於開啟狀態,PHPSESSID的值會儲存在記憶體中,一旦強行關閉,儲存PHPSESSID的cookie檔案會同時銷毀,但是服務端並沒有執行session_destroy(),所以,服務端的session資料檔案還在,但是當瀏覽器再次開啟執行程式4,發現什麼都沒有輸出,由此推理:


session_start()首先會去擷取用戶端cookie裡的PHPSESSID,然後與“sess_”組成檔案名稱,去服務端尋找這個檔案,然後取出檔案裡的內容,把內容放到$_SESSION全域變數裡以供使用。瀏覽器強行關閉,再開啟,之前的PHPSESSID丟失,這時遇到session_start()就相當於上面說的第一次執行,會產生一個新的PHPSESSID,這個PHPSESSID匹配不到之前那個服務端的sess_檔案,所以取不到內容。當然,服務端也有能跟這個PHPSESSID匹配的檔案,不過,那個檔案還是空的。


所以,有的系統為了實現同一使用者只能在一台機器甚至一個瀏覽器登入的機制,如果沒有修改session.cookie_lifetime的設定,就會出現強行關閉瀏覽器之後,在服務端session生存期截止前該,使用者登入不進去的情況,比較好的辦法是把session.cookie_lifetime設定成一個比較大的值,反正一個cookie檔案存在時間久一些也沒什麼影響。

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.