你知道什麼是 Session 嗎?為什麼需要它?它又是怎麼運作的呢? 今天就來聊聊 Session 吧!

HIHI~😍 如果你是第一次來的話,『Chan-Chan-Dev』是一個專門用簡單的圖文與故事講解網路程式技術的網站。
若你也喜歡用這種方式學習的話,歡迎加入 Chan-Chan-Dev Facebook 的粉絲團,在發佈的時候就有比較多機會收到通知喔!😍


身爲一個後端工程師,在學習的過程中一定都會經歷過 2 個關鍵字: Session & Cookie

也許許多的語言或者是套件已經幫我們完成了 Session 與 Cookie 的功能,我們需要的僅止於知道如何使用他們。然而剛好趁這次的機會來好好瞭解一下他們的使用方式與背後的原理吧。

前情提要

這篇是《 Session & Cookie 傻傻分不清楚》的下篇,簡單地介紹了 Session 是什麼與介紹情境。

若錯過上集的朋友,可以請往這邊走: Session & Cookie 傻傻分不清楚:Cookie 篇


在上一集我們有提到,因爲 HTTP 無狀態 的情況下,伺服器無法在這麼多個 HTTP 的請求分辨是哪些是屬於同一個使用者的!

HTTP無狀態所遇到的困境

🚀 希望達成效果 🚀

讓伺服器有記憶能力可以記住每一個使用者的狀態!!

之前的解決方案一是使用 Cookie 的方式。只是把資料用 Cookie 的附上存在使用者那邊會遇到幾個問題:

  • 存放資料不能太大(只有 4 kb 而已,還記得嗎?)
  • 使用者可以亂改 Cookie 上的資訊(小朋友不可以亂學喔)

解決方案二:直接幫使用者辦一張會員卡

那何不把資料都放在伺服器的某一個櫃子裡,然後產生一組會員卡卡號貼在這個櫃子外面,然後將這組會員卡卡號寫在小紙條上 (Cookie) 回覆給使用者,請他下次來的時候就帶這組卡號來就可以了?

使用者發起 HTTP 請求
Session HTTP發起請求階段

伺服器將使用者的資料存在一個櫃子裡,並起產生一組會員卡卡號貼在這個櫃子外面
Session 伺服器將使用者的資料存在一個櫃子裡,並起產生一組會員卡卡號貼在這個櫃子外面

伺服器將這組會員卡卡號寫在小紙條上 (Cookie) 回覆給使用者
Session 伺服器將這組會員卡卡號寫在小紙條上 Cookie 回覆給使用者

下次使用者只要連同這組卡號發起請求,伺服器就可以針對這組卡號去後面的櫃子找到他的資料囉
Session 使用者只要連同這組卡號發起請求
Session 伺服器就可以針對這組卡號去後面的櫃子找到他的資料

Session

Session-封面
上述講的方法就是使用 sessionID 的機制,將使用者的資料存放在伺服器端,然後產生一組 sessionID 辨認這些資料,並把這組 sessionIDCookie 的方式存放在使用者端。

因此下次使用者只要出示這張如同會員卡卡號的 sessionID ,伺服器就可以找到這個使用者的資料囉。

什麼是 Session 呢?

依據 wiki 的定義:

在電腦科學領域來說,尤其是在網路領域,對談(session,Microsoft Windows 中文版譯作工作階段)是一種持久網路協定,在使用者(或使用者代理)端和伺服器端之間建立關聯,從而起到交換封包的作用機制,session在網路協定(例如 telnet 或 FTP)中是非常重要的部分。

在不包含會話層(例如UDP)或者是無法長時間駐留會話層(例如 HTTP)的傳輸協定中,會話的維持需要依靠在傳輸資料中的進階別程式。例如,在瀏覽器和遠端主機之間的 HTTP 傳輸中,HTTP cookie 就會被用來包含一些相關的資訊,例如 session ID,參數和權限資訊等。

將 SessionID 傳遞給使用者的方式

上述的圖解中,是以 Cookie 的方式傳遞給使用者端,也是網路上比較常見的做法。例外一種做法則是透過 URL 進行傳遞,不過相較於第一種使用 Cookie 的做法比較不安全,所以較少使用 😅

Session 使用方式 (PHP)

開始 PHP Session 的起手式

1
session_start();

將資料存入 Session

Session 的資料可以存放在 PHP 的 $_SESSION 變數中,假如我們要將資料存入 Session:

1
2
3
4
5
session_start();

$_SESSION['web'] = 'Chan-Chan-Dev';
$_SESSION['url'] = 'https://chan-chan-dev.com';

取得之前存入 Session 的資料

因爲我們之前已經有將資料存入 Session。所以我們可以用以下的方式來取用

1
2
3
4
session_start();

echo $_SESSION['web']; // 讀取之前存取 key 爲 web 的內容物
echo $_SESSION['url']; // 讀取之前存取 key 爲 url 的內容物

若直接存取之前沒有存入的值會怎麼樣呢?😅

1
2
3
session_start();

echo $_SESSION['abc']; // 之前沒有存入 Key 爲 abc 的內容

恩… 就會得到以下的警告錯誤 😆

1
*Warning: Undefined array key "abc" in session-1.php on line 11*

因此爲了保險起見,可以在之前用 isset() 檢查是否有這個 key 的內容喔 😍

1
2
3
4
5
6
session_start();

if(isset($_SESSION['abc'])){ // 檢查之前是否有存入 Key 爲 abc 的內容
echo $_SESSION['abc'];
}

刪除 Session 的某一個資料

若要刪除某一個 key 的 key-value 資料的話,可以直接使用 unset() 從 Session 中移除,例如:

1
2
session_start();
unset($_SESSION['web']); // 移除 key 爲 web 的資料

註銷 Session

若因爲使用者登出,或者是其他原因需要把整個 Session 註銷的話,也可以使用 session_destroy() 註銷喔。

1
session_destroy();

Session 傳值

就如同上述所說的,伺服器會開啓一個櫃子,把使用者的資料放在裏面,並且有個專屬的會員卡卡號 (SessionID),所以因爲有了 Session 讓使用者的資料有一個暫時的存放位置。

那是不是也可以用這個儲存機制在不同的頁面傳遞資料呀?😍

第一次拜訪頁面 A ,將資料存入 Session 並且取得 SessionID

Session 第一次拜訪頁面 A ,將資料存入 Session 並且取得 SessionID

1
2
3
4
5
6
7
session_start();

// 在頁面 A 存入資料
$_SESSION['name'] = '小明';

echo "Here is Page A" . "<br>";

第二次用 SessionID 想要在頁面 B 取得當時後在頁面 A 存入的資料

Session 第二次用 SessionID 想要在頁面 B 取得當時後在頁面 A 存入的資料

1
2
3
4
5
6
7
8
session_start();

echo "Here is Page B" . "<br>";

if($_SESSION['name']){
// 在頁面 B 存取 頁面 A 存入 Session 的資料
echo $_SESSION['name'] . "<br>";
}

Session 的那個櫃子究竟存在哪裡呢? 🤔

Session 伺服器將使用者的資料存在一個櫃子裡,並起產生一組會員卡卡號貼在這個櫃子外面

我們之前提到伺服器會把資料存放在一個櫃子裡,而櫃子其實只是一個記錄存放的比喻啦,你懂的 😆

而每一個語言對於 Session 的實作都不太一樣,有些存在記憶體、有些則存在檔案裡。PHP 預設的情況下是存在 檔案 裡,若想看到 Session 相關的詳細設定,我們可以來 這裏 查看有哪些 Session 設定。

若我們真的想要一探究竟的話,首先我們得先找到電腦裏的 php.ini,若你跟我一樣也是使用 Mac 的終端機的話,可以試試看以下指令看看 php.ini到底身在何處。

1
2
3
$ php -i | grep php.ini
Configuration File (php.ini) Path => /usr/local/etc/php/8.0
Loaded Configuration File => /usr/local/etc/php/8.0/php.ini

用 vs code(或自己習慣的 IDE)開啓 php.ini 後,Cmd + F 搜尋 [Session] 相關的關鍵字,應該就可以找到相關的設定囉 😍

Session 用 vs code 開啓 php.ini

往下應該會找到 session.save_path 區塊,對應的值告訴我們 Session 存放在 /tmp 資料夾,這也是 PHP Session 預設存放的資料夾喔 🙂

1
2
; http://php.net/session.save-path
session.save_path = "/tmp"

於是乎我們在 /tmp 資料夾裏面找到了這個檔名看起來很奇怪的 sess_8vpvg3dg9m6n3kbgalni847n3q 檔案,原來這就是我們剛剛說的 SessionID 呀!!

1
2
3
.
├── .....
└── sess_8vpvg3dg9m6n3kbgalni847n3q

打開這個檔案後,真相大白!發現原來剛剛在 PHP 程式碼裏面存放在 Session 的資料是存在這裏呀! 🤩

1
web|s:13:"Chan-Chan-Dev";url|s:25:"https://chan-chan-dev.com";

對了 😀 ,還記得剛剛有提到註銷 Session 的指令嗎?

1
session_destroy();

使用這個指令的話,這個 sess_8vpvg3dg9m6n3kbgalni847n3q 檔案就會整個被刪除掉囉!

查看在 Cookie 的 Session ID

Session 伺服器將這組會員卡卡號寫在小紙條上 Cookie 回覆給使用者

上述也有提到伺服器會將得到 SessionIDCookie 的方式回傳給使用者,所以在使用者端的 SessionID 存放在哪裏呢? 🙂

我們可以簡單地用以下的步驟利用 Chrome 的 DevTools 查看這筆請求回覆的 Cookies,就會發現有一個叫做 PHPSESSID的記錄,他的 Value 就是我們上述的 SessionID sess_8vpvg3dg9m6n3kbgalni847n3q sess_ 之後的內容囉 😍

Session 用 DevTool 查看回傳的 SessionID

至於 PHPSESSID 的這個值是可以調整的嗎? 😆

剛剛在 php.ini 裏面其中有一個 session.name 的區塊,若有需要調整就可以更改這邊的設定即可囉~

1
2
; http://php.net/session.name
session.name = PHPSESSID

小結

就如同一開始的想要達成的效果:

讓伺服器有記憶能力可以記住每一個使用者的狀態!!

藉由 SessionCookie 的輔助下,伺服器終於有能力記住使用者的狀態了。只是他們存放的位置各有不同, Session 就如同上述提到的是伺服器開啓的一個櫃子,所以是存在伺服器端。而 Cookie 則是像是一張小紙條,會讓使用者帶回去存放在瀏覽器,所以是存放在使用者端,也因爲存放在使用者端相對的資料的安全更需要被注意,因此在 Cookie 儘可能地不要存放機敏資料,若有需要儘可能地存放在 Session 裡也許是一個較爲安全的做法喔 🙂


好囉,有關 Session 的討論就差不多告一個段落囉!
如果覺得喜歡這種圖文介紹的文章的話,歡迎加入 Chan-Chan-Dev Facebook 的粉絲團,在發佈的時候就有比較多機會收到通知喔!😍

最後也感謝你的閱讀,下次再見囉 😍