使用 cookie 和 session 來紀錄使用者狀態

前言

HTTP 本身是無狀態(Stateless)的通訊協定(Protocol)。
但是可以用 cookie & session 在客戶端與伺服器之間存取一些使用者狀態。

在講解如何處理使用者狀態之前,先簡單釐清 cookie & session。
cookie 是存在 client 端的,session 是存在 server 端的。

如何用 session 紀錄使用者狀態

想像去飲料店點飲料,店員發給你號碼牌,之後你可以用號碼牌來領你的飲料。
你就是 client,飲料店就是 server,號碼牌就是 session id。

換到真實的情境,server 端設定 session 開始後,會自動產生一個 session id。
這時候我們可以在 session 裡額外加上 user id。
接著在 response headers 會自動被加上下面內容,指示瀏覽器要存一個名為 PHPSESSID 的 cookie。

response header

1
Set-Cookie: PHPSESSID=8afd28b38e013478f508473db0dfd172; path=/

之後 client 端每次向 server 請求時就會夾帶著 cookie。

request header

1
Cookie: PHPSESSID=8afd28b38e013478f508473db0dfd172

server 端收到後就會根據 session id 來識別這是哪個 session 的請求,
這時候我們只要再看看 session 裡的 user id 就知道哪位 user 了。特別說明一下,中間 cookie 夾帶 session id 的傳遞都是背後會自動去執行的,server 和 client 都不用做額外的操作。

缺點:

  • session 過期前會一直佔用 server 資源

用 session 實作 login & logout

以下用 session 來實做一下使用者登入。

Login form

1
2
3
4
5
<form action="" method="post">
<input type="text" name="username" placeholder="Enter your username" required>
<input type="password" name="password" placeholder="Enter your password" required>
<input type="submit" value="Submit">
</form>

Process Login

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
session_start();

if ( ! empty( $_POST ) ) {
if ( isset( $_POST['username'] ) && isset( $_POST['password'] ) ) {
// Getting submitted user data from database
$db = new mysqli($db_host, $db_user, $db_pass, $db_name);
$sql = $db->prepare("SELECT * FROM users WHERE username = ?");
$sql->bind_param('s', $_POST['username']);
$sql->execute();
$result = $sql->get_result();
$user = $result->fetch_object();

// Verify user password and set $_SESSION
if ( password_verify( $_POST['password'], $user->password ) ) {
$_SESSION['user_id'] = $user->ID;
}
}
}
?>

Any protected page

1
2
3
4
5
6
7
8
9
10
<?php
session_start();

if ( isset( $_SESSION['user_id'] ) ) {
// Get user data if session exists
} else {
// Redirect to the login page
header("Location: https://www.yourdomain.com/login.php");
}
?>

Logout

1
2
3
<?php
session_start();
session_destroy();


紀錄使用者狀態也可以單純只使用 cookie,把 user 的身份和其他資訊都存在 cookie 裡,然後 server 就只看這個 cookie 去判斷你是誰。
當然儲存的資訊都會經過加密處理,但是還是有被破解的風險,而且 cookie 有儲存大小 4k 的限制,所以實務上有可能會 session 和 cookie-based session 配合使用。

缺點:

  • 不適合存敏感資訊

概念上 cookie 又分成兩種:

  • Persistent cookie

    就是明確指定 cookie 的存活時間,直到指定的時間為止,即便瀏覽器關閉 cookie 依然活著。

    1
    setcookie("TestCookie", $value, time() + 60 * 60 * 24);
  • Session cookie

    session cookie 則是相反,不指定存活時間,只活在這個瀏覽器的 session 期間,關閉瀏覽器後就被清除了。但是實測後發現如果在 chrome 上有勾選繼續瀏覽上次開啟的網頁(Continue where you letf off),session cookie 是不會被清除的。Ref

HTML5 新增的兩種儲存方式: local storage & session storage,下面把他們與 cookie 做一下比較。

Cookies Local Storage Session Storage
Capacity 4kb 10mb 5mb
Browsers HTML4/HTML5 HTML5 HTML5
Accessible from Any winodw Any winodw Same tab
Expires Manually set Never On tab close
Storage Location Browser and server Browser only Browser only
Sent with requests Yes No No
Restriction Same domain Same domain Same domain

Reference

Web 技術中的 Session 是什麼?

How to Build a PHP Login Form Using Sessions

cookies vs localStorage vs sessionStorage