Chương 8: Cookies, Session và Web Storage

Bối cảnh: Tại sao cần lưu trạng thái?

HTTP là giao thức stateless (phi trạng thái). Điều này có nghĩa là mỗi request từ trình duyệt đến server là hoàn toàn độc lập — server không nhớ bạn là ai sau khi xử lý xong request trước. Ví dụ: sau khi bạn đăng nhập vào Facebook, ngay request tiếp theo (đăng bài) thì server sẽ không biết bạn là ai nếu không có cơ chế lưu trạng thái.

Để giải quyết vấn đề này, web sử dụng 3 cơ chế chính: Cookies, Session, và Web Storage.


1. Cookies

Cookies là gì?

Cookie là một tập tin nhỏ (dưới 4KB) chứa thông tin liên quan đến người dùng, được lưu trên máy của người dùng (client-side) và có thể được quản lý bởi trình duyệt.

3 mục đích chính

Mục đíchMô tảVí dụ
Quản lý phiênGhi nhớ trạng thái người dùngGiỏ hàng, thông tin đăng nhập, điểm số game
Cá nhân hóaLưu tùy chọn giao diệnNgôn ngữ, theme màu sắc trang web
Theo dõi hoạt độngPhân tích hành viTần suất truy cập, trang đã xem

Cách hoạt động

Luồng hoạt động của cookie diễn ra như sau:

sequenceDiagram participant Browser as Trình duyệt (Client) participant Server as Máy chủ (Server) participant DB as Database Browser->>Server: HTTP Request (lần đầu tiên) Server->>DB: Tạo entry cho user (ID: 1678) Server-->>Browser: HTTP Response + Set-Cookie: user_id=1678 Note over Browser: Lưu cookie xuống máy Browser->>Server: HTTP Request tiếp theo + Cookie: user_id=1678 Server->>DB: Tra cứu thông tin user 1678 Server-->>Browser: HTTP Response (đã biết bạn là ai)

Giải thích chi tiết:

  1. Lần đầu truy cập, server tạo ra một định danh cho người dùng và gửi kèm trong Set-Cookie của HTTP Response Header.
  2. Trình duyệt nhận cookie và lưu lại trên máy.
  3. Từ các request tiếp theo, trình duyệt tự động đính kèm cookie vào HTTP Request Header — người dùng không cần làm gì.

Các loại Cookies

HTTP Headers liên quan đến Cookies

Server gửi cookie về client qua Set-Cookie trong Response Header:

HTTP/1.0 200 OK
Set-Cookie: SSID=Ap4PGTEq; Domain=foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly

Client gửi cookie lên server qua Cookie trong Request Header:

GET /page HTTP/1.1
Host: foo.com
Cookie: SSID=Ap4PGTEq; lang=vi-VN
Thuộc tínhMô tảVí dụ
Name=ValueTên và giá trị cookieASP.NET_SessionId=abcdjsa
Expires=dateNgày hết hạn. Nếu để trống → xóa khi đóng trình duyệtExpires=Wed, 09 Jun 2021 10:18:14 GMT
Max-Age=secondThời gian sống tính bằng giây (ưu tiên hơn Expires nếu cả hai cùng có)Max-Age=9000 (= 2.5 giờ)
Path=pathCookie chỉ gửi khi request đến path này hoặc path con của nóPath=/ (gửi cho tất cả path)
Domain=domainDomain nào được phép nhận cookieDomain=facebook.com
SecureChỉ gửi cookie qua kết nối HTTPSSecure
HttpOnlyCookie không thể truy cập bằng JavaScript (document.cookie), chỉ server mới đọc đượcHttpOnly
SameSiteKiểm soát khi nào cookie được gửi theo cross-site request (giúp chống CSRF)SameSite=Strict / Lax / None

Tạo cookie:

// Cách đơn giản
document.cookie = "user=UIT; expires=Wed, 13 Jan 2021 00:00:00 GMT";

// Hàm tổng quát để tạo cookie
function setCookie(cname, cvalue, exdays) {
    let d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    let expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

// Ví dụ sử dụng: lưu cookie tên "username" trong 7 ngày
setCookie("username", "John", 7);

Đọc cookie:

// Lấy toàn bộ cookie dưới dạng chuỗi
let allCookies = document.cookie;
// Kết quả: "user=UIT; lang=vi-VN; theme=dark"

// Hàm đọc cookie theo tên
function getCookie(cname) {
    let name = cname + "=";
    let decodedCookie = decodeURIComponent(document.cookie);
    let ca = decodedCookie.split(';');
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i].trim();
        if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

// Ví dụ
let username = getCookie("user"); // "UIT"

Thay đổi cookie (ghi đè bằng cùng tên):

document.cookie = "username=Jane; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/";

Xóa cookie (đặt expires về quá khứ):

document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";

2. Session

Session là gì?

Session là một phiên làm việc — một cơ chế lưu trữ thông tin người dùng ở phía server. Khi người dùng truy cập vào ứng dụng web, server tạo ra một “hồ sơ tạm thời” dành riêng cho người đó.

Vòng đời của Session:

  • Bắt đầu: Khi client gửi request lần đầu (hoặc sau khi đăng nhập thành công).
  • Tồn tại: Xuyên suốt quá trình người dùng dùng ứng dụng, từ trang này sang trang khác.
  • Kết thúc: Khi người dùng đóng trình duyệt, hoặc khi session timeout (hết giờ không hoạt động — thường từ 20–30 phút).

Session thường dùng Cookie để truyền Session ID giữa client và server:

sequenceDiagram participant Browser participant Server participant DB as Session Store (Server RAM / DB) Browser->>Server: POST /login (username, password) Server->>DB: Xác thực thông tin, tạo session mới DB-->>Server: Session ID = "abc123" Server-->>Browser: HTTP Response + Set-Cookie: session_id=abc123 Note over Browser: Lưu session_id vào cookie Browser->>Server: GET /profile + Cookie: session_id=abc123 Server->>DB: Tra cứu session "abc123" → lấy thông tin user DB-->>Server: {userId: 1, username: "john", role: "admin"} Server-->>Browser: Trả về trang profile đúng user

Kết quả: Server nhận ra bạn là ai mà không cần bạn gửi lại username/password mỗi lần.

Nên lưu gì vào Session?

Session được lưu trên server — mỗi người dùng chiếm một phần bộ nhớ server. Do đó, cần chọn lọc dữ liệu lưu trữ.

Nên lưu:

  • Thông tin đăng nhập (user ID, role, quyền hạn)
  • Giỏ hàng (với người dùng chưa đăng nhập)
  • Sản phẩm/nội dung đã xem gần đây
  • Dữ liệu wizard/form nhiều bước

Không nên lưu:

  • Dữ liệu lớn như danh sách sản phẩm, bài viết → gây tốn RAM server
  • Mật khẩu (dù đã hash)
  • Dữ liệu có thể lấy lại từ database dễ dàng

Session trong PHP

<?php
// Bước 1: Bắt đầu session (phải gọi trước khi output bất kỳ HTML nào)
session_start();

// Bước 2: Lưu dữ liệu vào session
$_SESSION["username"] = "john_doe";
$_SESSION["role"] = "admin";
$_SESSION["cart"] = ["item1", "item2"];

// Bước 3: Đọc dữ liệu từ session
echo "Xin chào " . $_SESSION["username"]; // "Xin chào john_doe"

// Bước 4: Xóa toàn bộ session (đăng xuất)
session_destroy();
?>

Session trong ASP.NET MVC

// Thêm mới (nếu chưa có) hoặc thay thế (nếu đã có)
Session["Username"] = "Huong Lan";
Session["CartCount"] = 3;

// Thêm mới (sẽ lỗi nếu key đã tồn tại)
Session.Add("Role", "Admin");

// Đọc dữ liệu
string name = Session["Username"]?.ToString();

// Lấy ID của session hiện tại
string sessionId = Session.SessionId;

// Xóa một key
Session.Remove("CartCount");

// Xóa toàn bộ session
Session.Abandon();

3. Web Storage

Web Storage là gì?

Web Storage là API của HTML5 cho phép ứng dụng web lưu trữ dữ liệu trực tiếp trong trình duyệt của người dùng, hoàn toàn phía client-side. Dữ liệu lưu theo dạng key-valuekhông bao giờ tự động gửi lên server như cookie.

Hai loại Web Storage

localStoragesessionStorage
Thời hạnVĩnh viễn (đến khi user xóa cache)Mất khi đóng tab
Phạm viTất cả tab/cửa sổ cùng originChỉ trong tab hiện tại
Dung lượng~5–10MB~5MB
Gửi lên serverKhôngKhông

Kiểm tra trình duyệt có hỗ trợ không

if (typeof(Storage) !== "undefined") {
    console.log("Trình duyệt hỗ trợ Web Storage!");
    // Tiến hành sử dụng localStorage / sessionStorage
} else {
    console.log("Trình duyệt không hỗ trợ Web Storage.");
    // Dự phòng: dùng cookie hoặc thông báo người dùng
}

Các phương thức của Web Storage API

Cả localStoragesessionStorage đều dùng chung bộ API sau:

Phương thứcMô tả
setItem(key, value)Lưu một cặp key/value. Nếu key đã tồn tại thì ghi đè
getItem(key)Lấy giá trị theo key. Trả về null nếu không tồn tại
removeItem(key)Xóa một cặp key/value theo key
clear()Xóa tất cả key/value trong storage
key(n)Lấy tên của key ở vị trí thứ n (dùng để duyệt qua tất cả key)
lengthSố lượng key/value đang được lưu

localStorage — Lưu trữ vĩnh viễn

// === Lưu dữ liệu ===
// Cách 1: setItem (khuyến nghị)
localStorage.setItem("theme", "dark");
localStorage.setItem("language", "vi");

// Cách 2: dot notation
localStorage.theme = "dark";

// Cách 3: bracket notation
localStorage["theme"] = "dark";

// === Lưu object (cần JSON.stringify) ===
const user = { name: "John", age: 25 };
localStorage.setItem("user", JSON.stringify(user));

// === Đọc dữ liệu ===
let theme = localStorage.getItem("theme"); // "dark"

// Đọc object
let storedUser = JSON.parse(localStorage.getItem("user"));
console.log(storedUser.name); // "John"

// === Xóa dữ liệu ===
localStorage.removeItem("theme");   // Xóa một key
localStorage.clear();               // Xóa tất cả

// === Duyệt tất cả key ===
for (let i = 0; i < localStorage.length; i++) {
    let key = localStorage.key(i);
    let value = localStorage.getItem(key);
    console.log(key + ": " + value);
}

sessionStorage — Lưu trữ theo phiên

API giống hoàn toàn localStorage, chỉ khác ở vòng đời:

// Lưu dữ liệu form (tránh mất khi refresh trang)
sessionStorage.setItem("draftEmail", "Kính gửi...");

// Lưu bước hiện tại của wizard nhiều bước
sessionStorage.setItem("checkoutStep", "3");

// Đọc lại
let draft = sessionStorage.getItem("draftEmail");

// Khi submit form xong, xóa draft
sessionStorage.removeItem("draftEmail");

4. So sánh tổng quan

Session vs Cookies

Tiêu chíSessionCookie
Nơi lưu trữServerClient (máy người dùng)
Dung lượngTùy ý (giới hạn bởi RAM/disk server)Tối đa 4KB
Thời hạnĐến khi đóng trình duyệt hoặc timeoutTùy thuộc vào Expires/Max-Age
Bảo mậtCao hơn (dữ liệu ở server)Thấp hơn (dữ liệu ở client, dễ đánh cắp/sửa)
Hiệu năng serverTốn RAM server khi nhiều người dùngKhông tốn tài nguyên server
Gửi lên serverGián tiếp (qua Session ID)Tự động kèm theo mỗi request

Cookies vs localStorage vs sessionStorage

Tiêu chíCookieslocalStoragesessionStorage
Dung lượng4KB~10MB~5MB
Hỗ trợ HTMLHTML4 +HTML5HTML5
Thời hạnDo lập trình viên đặtVĩnh viễn (đến khi xóa cache)Đến khi đóng tab
Truy cập từBất kỳ tab nào (cùng domain)Bất kỳ tab nào (cùng origin)Chỉ trong tab hiện tại
Gửi lên serverCó (tự động)KhôngKhông
Nơi lưuBrowser + Server nhậnBrowser onlyBrowser only
JavaScript đọc đượcCó (trừ khi HttpOnly)

5. Câu hỏi ôn tập