1. 程式人生 > >PHP中session和cookie的用法及區別

PHP中session和cookie的用法及區別

一,session

1.session 是啥?

2.怎麼儲存的?

3.如何執行?

4.有生命週期嗎?

5.關閉瀏覽器會過期嗎?

6.Redis代替檔案儲存session

7.分散式session的同步問題

 

 

session是啥?

 

首先,我大致的知道,session是一次瀏覽器和伺服器的互動的會話,會話是啥呢?就是我問候你好嗎?你回恩很好。就是一次會話,那麼對話完成後,這次會話就結束了,還有我也知道,我們可以將一個變數存入全部的$_SESSION['name']中,這樣php的各個頁面和邏輯都能訪問到,所以很輕鬆的用來判斷是否登陸。

 

這是我之前理解的session,當然也是對的,只是解釋的太膚淺,理解的太表面了,面試官如果聽到這樣的答案其實是不太滿意的。我參考了其他的很多資料,徹底理解清楚session。

 

在說session是啥之前,我們先來說說為什麼會出現session會話,它出現的機理是什麼?我們知道,我們用瀏覽器開啟一個網頁,用到的是HTTP協議,學過計算機的應該都知道這個協議,它是無狀態的,什麼是無狀態呢?就是說這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的。但是這種無狀態的的好處是快速。

 

所以就會帶來一個問題就是,我希望幾個請求的頁面要有關聯,比如:我在www.a.com/login.php裡面登陸了,我在www.a.com/index.php 也希望是登陸狀態,但是,這是2個不同的頁面,也就是2個不同的HTTP請求,這2個HTTP請求是無狀態的,也就是無關聯的,所以無法單純的在index.php中讀取到它在login.php中已經登陸了!

 

那咋搞呢?我不可能這2個頁面我都去登陸一遍吧。或者用笨方法這2個頁面都去查詢資料庫,如果有登陸狀態,就判斷是登陸的了。這種查詢資料庫的方案雖然可行,但是每次都要去查詢資料庫不是個事,會造成資料庫的壓力。

 

所以正是這種訴求,這個時候,一個新的客戶端儲存資料方式出現了:cookie。cookie是把少量的資訊儲存在使用者自己的電腦上,它在一個域名下是一個全域性的,只要設定它的儲存路徑在域名www.a.com下 ,那麼當用戶用瀏覽器訪問時,php就可以從這個域名的任意頁面讀取cookie中的資訊。所以就很好的解決了我在www.a.com/login.php頁面登陸了,我也可以在www.a.com/index.php獲取到這個登陸資訊了。同時又不用反覆去查詢資料庫。

 

雖然這種方案很不錯,也很快速方便,但是由於cookie 是存在使用者端,而且它本身儲存的尺寸大小也有限,最關鍵是使用者可以是可見的,並可以隨意的修改,很不安全。那如何又要安全,又可以方便的全域性讀取資訊呢?於是,這個時候,一種新的儲存會話機制:session 誕生了。

 

我擦,終於把session是怎麼誕生的給圓清楚了,不容易啊!!!

 

好,session 誕生了,從上面的描述來講,它就是在一次會話中解決2次HTTP的請求的關聯,讓它們產生聯絡,讓2兩個頁面都能讀取到找個這個全域性的session資訊。session資訊存在於伺服器端,所以也就很好的解決了安全問題。

 

session的執行機制和是怎麼儲存的?

 

既然,它也是一種服務區儲存資料的方式,肯定也是存在伺服器的某個地方了。確實,它存在伺服器的/tmp 目錄下,這一點我們接下來慢慢講。

 

我們先說下它的執行機制,是怎麼分配的。我們主要用PHP中session的機制,其實各種語言都差不多。

 

如果這個時候,我們需要用到session,那我們第一步怎麼辦呢?第一步是開啟session:

session_start();

 

這是個無任何返回值的函式,既不會報錯,也不會成功。它的作用是開啟session,並隨機生成一個唯一的32位的session_id,類似於這樣:

 

4c83638b3b0dbf65583181c2f89168ec

session的全部機制也是基於這個session_id,它用來區分哪幾次請求是一個人發出的。為什麼要這樣呢?因為HTTP是無狀態無關聯的,一個頁面可能會被成百上千人訪問,而且每個人的使用者名稱是不一樣的,那麼伺服器如何區分這次是小王訪問的,那次是小名訪問的呢?所以就有了找個唯一的session_id 來繫結一個使用者。一個使用者在一次會話上就是一個session_id,這樣成千上萬的人訪問,伺服器也能區分到底是誰在訪問了。

 

我們做個試驗,看看,是不是這樣的:

 

我們在php.iyangyi.com 域名下的a.php 頁面中,輸入如下程式碼:

session_start(); echo "SID: ".SID."<br>"; echo "session_id(): ".session_id()."<br>"; echo "COOKIE: ".$_COOKIE["PHPSESSID"];

 

我們訪問一下a.php頁面,看能輸出什麼?

 

我們看到居然還有一個警告。我們先一個一個的看。首先SID這個常量,我們沒有給它賦值,它居然能有輸出,其次session_id()這個系統方法是輸出本次生成的session_id。最後$_COOKIE['PHPSESSIID'] 沒有值,這個我們接下來說。

 

好,我們再次重新整理這個頁面,我們能看到什麼?

 

奇怪的事情發生了。SID 沒有值了,$_COOKIE['PHPSESSID']中有值了。而且,2次重新整理,session_id 都是一樣

的:bjvlo4p38cfqkr1hr7pe924ts3,實際情況下,只要不關閉網頁,怎麼重新整理都是一樣:

 

既然我們看到COOKIE中有值了,我們,開啟firebug開看到底是什麼:

 

而且這個PHPSESSID的過期時間是會話,什麼意思呢?就是瀏覽器只要不關就一直不存,瀏覽器一關就過期,消失了。

 

好,我們關掉瀏覽器,重新開啟a.php頁面,看看有沒有什麼變化:

 

 

你看,是不是又回到當初第一次開啟時候的樣子。

 

OK,解惑的時候到了:

 

每次我們訪問一個頁面,如果有開啟session,也就是有session_start() 時,就會自動生成一個session_id 來標註是這次會話的唯一ID,同時也會自動往cookie裡寫入一個名字為PHPSESSID的變數,它的值正是session_id,當這次會話沒結束,再次訪問的時候,伺服器會去讀取這個PHPSESSID的cookie是否有值有沒過期,如果能夠讀取到,則繼續用這個session_id,如果沒有,就會新生成一個session_id,同時生成PHPSESSID這個cookie。由於預設生成的這個PHPSESSID cookie是會話,也就是說關閉瀏覽器就會過期掉,所以,下次重新瀏覽時,會重新生成一個session_id。

 

好,這個是session_id,就用來標識繫結一個使用者的,既然session_id生成了。那麼當我們往session裡面寫入資料,是如何儲存的,答案是儲存在伺服器的臨時目錄裡,根據php.ini的配置,我機子上的這個session是存在D:\wamp\tmp 目錄裡的。我們先說是存在這個目錄下,然後待會將如何修改。

 

那麼它是怎麼存的呢?

 

同樣也是用到session_id。session_id是32位的,伺服器會用 sess_字首 + session_id 的形式存在這個臨時目錄下,比如上面這個例子:

 

 

 

所以,每一次生成的session_id都會生成一個這樣的檔案,用來儲存這次會話的session資訊。

 

我們往session裡寫入些資料,來看看session是怎麼往這個檔案裡寫資料的,我們同樣在a.php頁面繼續加上寫入session的語句:

$_SESSION['hello'] = 123;
$_SESSION['word'] = 456;
 

然後,我重新整理頁面,由於我並沒有關閉頁面,就這是說這次會話還沒結束,那麼肯定還會是同樣的session_id : bjvlo4p38cfqkr1hr7pe924ts3

 

然後,我們 用編輯器開啟它的儲存檔案sess_bgg20mcl86drbt3j08jg5h5h17這個檔案,看看裡面是啥?

hello|i:123;word|i:456;
 

是序列化的資料,我們肉眼也能讀出來。當我們往$_SESSION全域性變數裡寫資料時,它會自動往這個檔案裡寫入。讀取session的時候,也會根據session_id 找到這個檔案,然後讀取需要的session變數。

 

這個sess檔案不會隨著客戶端的PHPSESSID過期,也一起過期掉,它會一直存在,出息GC掃描到它過期或者使用session_destroy()函式摧毀,我們在下面講到session·回收的時候會說到。

 

我們大致總結下:

 

HTTP請求一個頁面後,如果用到開啟session,會去讀cookie中的PHPSESSID是否有,如果沒有,則會新生成一個session_id,先存入cookie中的PHPSESSID中,再生成一個sess_字首檔案。當有寫入$_SESSION的時候,就會往sess_檔案裡序列化寫入資料。當讀取的session變數的時候,先會讀取cookie中的PHPSESSID,獲得session_id,然後再去找這個sess_sessionid檔案,來獲取對應的資料。由於預設的PHPSESSID是臨時的會話,在瀏覽器關閉後,會消失,所以,我們重新訪問的時候,會新生成session_id和sess_這個檔案。

 

好。session生成和儲存將清楚了。我們再來看前面提到的幾個變數:

 

echo "SID: ".SID."<br>";

echo "session_id(): ".session_id()."<br>";

echo "COOKIE: ".$_COOKIE["PHPSESSID"];
 

SID 是一個系統常量,SID包含著會話名以及會話 ID 的常量,格式為 "name=ID",或者如果會話 ID 已經在適cookie 中設定時則為空字串,第一次顯示的時候輸出的是SID的值,當你重新整理的時候,因為已經在cookie中存在,所以顯示的是一個空字串。

 

session_id() 函式用來返回當前會話的session_id,它會去讀取cookie中的name,也就是PHPSESSID值。

 

 

session的相關配置

 

上面巴拉巴拉廢話說了那麼多,應該是可以理解session的一套機制了的,我接下來看看,前面零星的提到了php.ini裡面有關於session相關的配置。我們開啟php.ini來,搜尋session相關,我主要把用到的幾個給列出來:

[Session]

session.save_handler = files

session.save_path = "d:/wamp/tmp"

session.use_cookies = 1

session.name = PHPSESSID

session.auto_start = 0

session.cookie_lifetime = 0

session.serialize_handler = php

session.gc_divisor = 1000

session.gc_probability = 1

session.gc_maxlifetime = 1440

 

主要我們用到的,常見的大概就是這幾個。我們一個一個的說。

 

session.save_handler = files 表示的是session的儲存方式,預設的是files檔案的方式儲存,sess_efdsw34534efsdfsfsf3r3wrresa, 儲存在 session.save_path = "d:/wamp/tmp" 裡,所有這2個都是可配值得。我們上面的例子就是用的這種預設的方式。

 

save_handler 不僅僅只能用檔案files,還可以用我們常見的memcache 和 redis 來儲存。這個我們後面來說。

 

session.use_cookies 預設是1,表示會在瀏覽器裡建立值為PHPSESSID的session_id,session.name = PHPSESSID 找個配置就是改這個名字的,你可以改成PHPSB, 那這樣就再瀏覽器裡生成名字為PHPSB的session_id 。`(*∩_∩*)′

 

session.auto_start = 0 用來是否需要自動開啟session,預設是不開啟的,所有我們需要在程式碼中用到session_start();函式開啟,如果設定成1,那麼session_id 也會自動就生成了。

 

session.cookie_lifetime = 0 這個是設定在客戶端生成PHPSESSID這個cookie的過期時間,預設是0,也就是關閉瀏覽器就過期,下次訪問,會再次生成一個session_id。所以,如果想關閉瀏覽器會話後,希望session資訊能夠保持的時間長一點,可以把這個值設定大一點,單位是秒。

 

gc_divisor, gc_probability, gc_maxlifetime 這3個也是配合一起使用,他們是幹嘛的呢?他們是幹大事情的,回收這些sess_xxxxx 的檔案,它是按照這3個引數,組成的比率,來啟動GC刪除這些過期的sess檔案。gc_maxlifetime是sess_xxx檔案的過期時間。具體可以參考這個,我覺得他說我比我清楚: session的垃圾回收機制

 

session的垃圾回收

 

我們通過上面的各種,已經清楚session的種種了,它會產生各種的sess_字首的檔案,久而久之就會形成垃圾資料,而且正常的session讀取也會造成壓力,所以及時的清理是蠻有用的。

 

1. 程式碼處理

 

php程式碼中有幾個函式是用來清理過期的session資訊的,主要是這幾個:

unset($_SESSION['hello']);
session_unset();
session_destroy();
setcookie(session_name(), '', time()-42000, '/');
 

unset 這是是常用的銷燬標量的方法,不多說,唯一要說的是刪除session ,就是將這個sess_xxx的檔案的hello變數給刪除了,其他的變數該有的都儲存著。而 session_unset() 這個不穿引數,這個是銷燬sess_xxx檔案中的所有變數,但是這個sess_xxx檔案還是儲存著。而session_destroy 則更狠角了,它是直接將這個sess_xxx檔案給刪掉。

 

一般退出操作裡面,我們也會將session_name() 獲得到的PHPSESSID也給過期掉,刪掉,因為網頁沒關,不這樣刪除的話,重新整理之後,找個值是存在的,伺服器將會重新建立一個一模一樣session_id的sess檔案。

 

2. php gc 自動刪除

 

php.ini中的幾個銷燬sess_xxx檔案的配置,在上面說了:

session.gc_divisor = 1000
session.gc_probability = 1
session.gc_maxlifetime = 1440
 

簡單說下,其實上面的一個超連結的部落格講的很清楚了,php觸發gc刪除過期的sess_x的檔案的概念是這樣計算的:概率= gc_probability/gc_divisor,上面的預設的引數,也就是說概念是1/1000的概念,在頁面啟動session_start() 函式時候,會觸發gc刪除過期的sess_檔案。這個概率其實是蠻小的

 

所以,我們可以將這個概念調整大一點,比如:將gc_probability 也調成1000,那gc_probability/gc_divisor 就等於1了,也就是百分一百會觸發。這樣就垃圾回收概率就大的多。

 

 

用redis儲存session

 

上面七七八八說了很多關於session的儲存啊機制啊等。現在說說如果用redis 儲存session。之前說的都是用檔案files儲存,現在想用redis,好處有哪些?

  1. 更快的讀取和寫入速度。redis是直接操縱記憶體資料的,肯定是要比檔案的形式快很多。
  2. 更好的設定好過期時間。檔案儲存的sess_sdewfrsf檔案其實被刪除掉還是要考運氣的和概率的,很有可能造成sess_檔案沒即時刪除,造成儲存磁碟空間過多,和讀取SESSION就變慢了。
  3. 更好的分散式同步。設定redis 的主從同步,可以快速的同步session到各臺web伺服器,比檔案儲存更加快速。

總的說來,用redis來儲存SESSION速度更快,效能更高。

 

要做的第一件事,當然就是安裝redis了。具體安裝和配置php與redis,就不細說了,可以參考我寫的redis相關:redis安裝與配置

 

redis 安裝好了之後,接下來就是修改php.ini了。將原來的files 改成redis:

session.save_handler = redis

session.save_path = "tcp://127.0.0.1:6381"
 

需要用到tcp來連線redis,如果你設定reids 有密碼訪問的話,這樣加上就可以了:tcp://127.0.0.1:6381?auth=authpwd

 

重啟web伺服器後,你就可以正常使用SESSION了。和之前使用files儲存SESSION一模一樣。

 

我們看下redis 是怎麼儲存session的。它是用了有別於檔案儲存使用sess_字首的名字,它用PHPREDIS_SESSION: 字首,再加上session_id 的形式,是一個string 字串型別,帶生存週期的。

 

PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5

 

 

你會發現,它的值和檔案儲存session一模一樣,都是用php序列化後儲存,而且有明確的過期時間,是根據配置:session.gc_maxlifetime = 1440 來設定的,預設1440秒。當然你可以修改成其他的。

 

我們寫入和讀取每頁還是一模一樣,包括刪除和情況,都是一模一樣,沒有什麼變化:

 

session_start(); //開啟session,如果讀不到cookie,會重新生成一個session_id,redis裡面也會新生成一個。

echo "SID: ".SID."<br>";

echo "session_id(): ".session_id()."<br>";

echo "COOKIE: ".$_COOKIE["PHPSESSID"];

$_SESSION['hello'] = 123; // 寫入session 。會序列化後寫入redis中
$_SESSION['word'] = 456;

var_dump($_SESSION['word']);  //讀session。會從redis讀到,解序列後,讀出這個值。redis 1440秒過期後,將讀不到。
unset($_SESSION['hello']);  // 刪除 hello 的session 。會刪除 redis的hello值
session_unset();  // 清空redis 中這個session_id的所有值。
session_destroy(); // 刪除掉這個PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5 key。

session同步

 

在做了web集群后,你肯定會首先考慮session同步問題,因為通過負載均衡後,同一個IP訪問同一個頁面會被分配到不同的伺服器上,如果不同的伺服器用的是不同的reidis服務,那麼可能就會出現,一個登入使用者,一會是登入狀態,一會又不是登入狀態。所以session這個時候就要同步了。剛好,我們選擇用redis作為了儲存,是可以在多臺redis 伺服器中同步的。

 

具體可以搜尋 reidis主從同步或者redis 叢集

二,cookie

   1.  認識cookie

        Cookie英文是曲奇餅、小甜品的意思,在web應用中,也類似於程式大餐中的小甜品,在 Internet 中,Cookie 實際上是指小量資訊,是由 Web 伺服器建立的,將資訊儲存在使用者計算機上的檔案,主要作用是記錄web應用中使用者的基本資訊,也可以叫做瀏覽器快取或Cookies。

        web應用都是基於http協議進行網路互動,而http協議對每個web頁面提交的資訊不會進行記錄,而很多網站都有會員功能,如果每次跳轉頁面的時候都重新提交一次會員資訊,那這個工作任務就太繁瑣,因此有了Cookie的誕生。web應用程式可以在操作時,由伺服器端程式碼生成一個Cookie資訊儲存在客戶端的瀏覽器中,生成一個很小的檔案,此時的檔案存在RAM執行記憶體中,如果使用者關閉瀏覽器會自動刪除Cookie資訊;如果程式生成一個帶有時間設定的cookie,那麼cookie檔案將存在實體記憶體中,下次訪問同一個web應用時,在設定時間範圍內,web應用仍能獲取到cookie資訊。

    2.  使用cookie

        (1)建立Cookie值

//  函式設定Cookie
Setcookie("標識鍵值","Cookie值","有效時間","有效作用域 / 表示當前網站下好使");
 
//  賦值設定Cookie
$_COOKIE['標識鍵值'] = "Cookie值";
        (2)修改Cookie值

//  函式設定Cookie
Setcookie("標識鍵值","Cookie新值","新有效時間","新的有效作用域");
 
//  賦值設定Cookie
$_COOKIE['標識鍵值'] = "Cookie新值";
        (3)刪除Cookie值

//  函式設定Cookie
Setcookie("標識鍵值","空值",time()-1);
        (4)檢視Cookie值

//  檢視所有Cookie值
var_dump($_COOKIE);
 
//  檢視具體的Cookie值
var_dump($_COOKIE['鍵值']);
         (5)使用舉例
<?php
    // 增加 Cookie
 
    // 設定Cookie函式法 setCookie();
    setCookie("test1","test1");
    // 設定Cookie賦值法
    $_COOKIE['test2'] = "test2";
 
    // 修改Cookie
 
    // 修改Cookie setCookie()
    setCookie("test1","test1->test3");
    // 修改Cookie賦值法
    $_COOKIE["test2"] = "test2->test4";
 
 
    // 查詢所有Cookie值
    var_dump($_COOKIE);
 
    // 查詢鍵值為test2的值
    var_dump($_COOKIE['test2']);
 
    // 刪除鍵值為test1的值
    setCookie("test1","",time()-1);
 
    // 設定test2值為空
    $_COOKIE['test2'] = "";
 
    var_dump($_COOKIE);
    3.  Cookie的缺點

        (1)Cookie相對不安全,容易被盜取,洩露個人隱私;

        (2)Cookie必須依賴於客戶端,客戶端(瀏覽器)可以關閉Cookie儲存功能,如果客戶端關閉Cookie,則web應用無法儲存Cookie值在客戶端本地。   

    4.  注意事項

        (1)使用setCookie設定或修改cookie值後,在第一次重新整理頁面時列印Cookie不會有結果,只在本地生成Cookie檔案,第二次重新整理的時候才可以獲取新增或修改的值。如果使用$_COOKIE新增值或者修改值,在修改後第一次請求就可以獲取到結果。

        (2)使用setCookie刪除Cookie值時,第一次刪除只是刪除了檔案,在當前程式碼中列印Cookie資料仍然可以打印出Cookie值,重新整理頁面,重新訪問後,對應的Cookie值已被刪除。

 !所有隻要是setCookie函式作用的值,第一次訪問不會產生對應的反應,第二次訪問會產生效果。而$_COOKIE操作的資料會在第一次訪問直接產生效果。

session與cookie的區別:

  1 session儲存在伺服器,客戶端不知道其中的資訊;cookie儲存在客戶端,服務端可以知道其中的資訊

  2 session中儲存的是物件,cookie中儲存的是字串

  3 session不能區分路徑,同一個使用者在訪問一個網站期間,所有的session在任何一個地方都可以訪問到;而cookie中如果設定了路徑引數,那麼同一個網站中不同路徑下的cookie互相是訪問不道德

session與cookie的聯絡:

  session是需要藉助cookie才能正常工作的,如果客戶端完全禁止cookie,session將失效

session和cookie的應用:

如何利用實現自動登入
  當用戶在某個網站註冊後,就會收到一個惟一使用者ID的cookie。客戶後來重新連線時,這個使用者ID會自動返回,伺服器對它進行檢查,確定它是否為註冊使用者且選擇了自動登入,從而使使用者無需給出明確的使用者名稱和密碼,就可以訪問伺服器上的資源

會話跟蹤:

  通常session cookie是不能跨視窗使用的,當你新開了一個瀏覽器視窗進入相同頁面時,系統會賦予你一個新的sessionid,這樣我們資訊共享的目的就達不到了,此時我們可以先把sessionid儲存在persistent cookie中,然後在新視窗中讀出來,就可以得到上一個視窗SessionID了,這樣通過session cookie和persistent cookie的結合我們就實現了跨視窗的session tracking

四、參考文獻

    1.  session和cookie的區別

    2.  session是什麼

    3.  Session是怎麼實現的?儲存在哪裡?

    4.  Cookie使用時需要注意個數及大小限制

    5.  HTTP Header 詳解