JWT vs. Cookie、Session
一、什麼是 JWT 和 Cookie Session?
1. JWT(JSON Web Token)
-
定義:JWT 是一種開放標準(RFC 7519),用於在客戶端和伺服器之間傳遞資訊的 token。它是一個由三部分組成的字串:Header(標頭)、Payload(負載)和 Signature(簽章),以.分隔,格式如下:
Header.Payload.Signature;
每個部分都經過 Base64 編碼,Payload 通常包含使用者資訊(如 ID、角色等),Signature 用來驗證 token 的完整性。
-
運作方式:
-
使用者登入後,伺服器生成一個 JWT,包含使用者的資訊並用私鑰簽署。
-
伺服器將 JWT 傳回給客戶端(通常放在 HTTP 回應的 body 或標頭)。
-
客戶端(通常是前端)將 JWT 儲存在本地(例如 localStorage 或 sessionStorage),之後每次發送 API 請求時,將 JWT 放在 HTTP 標頭(通常是 Authorization: Bearer
<token>
)中。 -
伺服器收到請求後,驗證 JWT 的簽章是否有效,並從 Payload 提取使用者資訊,無需查詢資料庫。
-
-
程式碼範例(前端發送 JWT 請求):
// 假設已經從伺服器取得 JWT 並儲存在 localStorage
const token = localStorage.getItem("jwt_token");
// 發送 API 請求時帶上 JWT
fetch("https://api.example.com/protected", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("錯誤:", error));
2. Cookie Session
-
定義:Cookie Session 是一種基於伺服器端會話(Session)的身份驗證方式。伺服器在使用者登入後生成一個唯一的 Session ID,儲存在伺服器的資料庫或記憶體中,並將 Session ID 存入客戶端的 Cookie 中。
-
運作方式:
-
使用者登入後,伺服器生成一個唯一的 Session ID,並將其與使用者資訊儲存在伺服器端(例如 Redis 或資料庫)。
-
伺服器將 Session ID 寫入客戶端的 Cookie(通常設置為 HttpOnly 和 Secure)。
-
客戶端每次發送請求時,瀏覽器會自動將 Cookie 附在 HTTP 請求中。
-
伺服器根據 Cookie 中的 Session ID 查詢對應的使用者資訊,驗證身份。
-
-
程式碼範例(前端無需特別處理 Cookie,瀏覽器自動處理):
// 假設伺服器已設置好 Cookie,發送請求時瀏覽器會自動帶上
fetch("https://api.example.com/protected", {
method: "GET",
credentials: "include", // 確保 Cookie 隨請求發送
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("錯誤:", error));
二、JWT 與 Cookie Session 的主要差異
以下是兩者的核心差異,分成幾個面向來比較:
面向 | JWT | Cookie Session |
---|---|---|
儲存方式 | 客戶端儲存(通常在 localStorage 或 sessionStorage)。 | Session ID 儲存在客戶端的 Cookie,實際資料儲存在伺服器端。 |
資料內容 | JWT 包含使用者資訊(Payload),無需伺服器儲存狀態(無狀態)。 | Session ID 僅為一串隨機字串,實際使用者資訊儲存在伺服器(有狀態)。 |
驗證方式 | 伺服器驗證簽章,無需查詢資料庫,適合分散式系統。 | 伺服器需根據 Session ID 查詢資料庫或記憶體,確認使用者身份。 |
安全性 | 依賴簽章保護,需妥善儲存避免 XSS 攻擊(例如竊取 localStorage)。 | Cookie 可設為 HttpOnly 和 Secure,可防 XSS,但需防 CSRF 攻擊。 |
效能 | 無需伺服器儲存狀態,減少資料庫查詢,適合高流量系統。 | 伺服器需儲存 Session 資料,隨著使用者增加,可能增加伺服器負擔。 |
有效期限 | 通常有明確過期時間(由 Payload 的 exp 欄位設定)。 | Session 有效期限由伺服器控制,Cookie 可設置過期時間。 |
跨域支援 | 需明確在請求標頭中加入 JWT,適合 API 驅動的應用程式。 | Cookie 可自動隨請求發送,適合傳統網頁應用,但跨域需設置 CORS。 |
三、優缺點比較
JWT 的優點
-
無狀態設計:伺服器不需儲存使用者狀態,適合分散式系統或微服務架構。
-
高效能:無需查詢資料庫,驗證速度快。
-
靈活:Payload 可包含自訂資訊(如角色、權限),方便前端或後端直接使用。
-
跨平台:適用於行動應用程式、單頁應用(SPA)等。
JWT 的缺點
-
安全性風險:若 JWT 被竊取(例如透過 XSS 攻擊),攻擊者可直接使用直到過期。
-
無法輕易撤銷:一旦發放,JWT 在有效期內無法主動失效,除非使用黑名單機制。
-
Payload 容量限制:JWT 不宜包含過多資料,否則 token 會變得過長,增加傳輸負擔。
Cookie Session 的優點
-
安全性較高:Cookie 可設為 HttpOnly,防止 XSS 攻擊;伺服器端可隨時終止 Session。
-
簡單易用:瀏覽器自動處理 Cookie,前端開發者無需額外管理。
-
靈活控制:伺服器可隨時更新或刪除 Session 資料。
Cookie Session 的缺點
-
伺服器負擔:需要儲存大量 Session 資料,隨著使用者增加,可能需要高效的資料庫或快取系統(如 Redis)。
-
跨域限制:跨域請求需額外設置 CORS 和 SameSite 屬性,否則可能無法正常工作。
-
不適合分散式系統:Session 資料通常集中儲存,擴展性不如 JWT。
四、何時使用 JWT 或 Cookie Session?
-
使用 JWT 的場景:
-
單頁應用(SPA)或行動應用程式,需與 RESTful API 互動。
-
微服務架構或分散式系統,伺服器間無需共享 Session 資料。
-
需要高效能且不依賴伺服器儲存狀態。
-
範例:React 或 Vue 應用程式搭配無狀態 API。
-
-
使用 Cookie Session 的場景:
-
傳統的伺服器渲染網頁應用(如 PHP、Ruby on Rails)。
-
對安全性要求較高,且希望伺服器完全控制使用者狀態。
-
需要頻繁更新或終止使用者 Session。
-
範例:傳統網頁應用或需要 CSRF 防護的系統。
-
五、程式碼實例:JWT vs Cookie Session
JWT 實例(前端 + 後端簡單驗證)
-
前端(儲存和使用 JWT):
// 登入後儲存 JWT
async function login(username, password) {
const response = await fetch("https://api.example.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
const data = await response.json();
if (data.token) {
localStorage.setItem("jwt_token", data.token); // 儲存 JWT
console.log("登入成功!");
}
}
// 發送受保護的請求
async function getProtectedData() {
const token = localStorage.getItem("jwt_token");
const response = await fetch("https://api.example.com/protected", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
console.log("受保護資料:", data);
}
// 使用範例
login("user", "password123");
getProtectedData(); -
後端(Node.js 簡單驗證 JWT,假設使用 jsonwebtoken 套件):
const jwt = require("jsonwebtoken");
const secret = "your-secret-key"; // 簽章用的私鑰
// 登入生成 JWT
app.post("/login", (req, res) => {
const { username, password } = req.body;
// 假設驗證成功
const payload = { username, role: "user" };
const token = jwt.sign(payload, secret, { expiresIn: "1h" });
res.json({ token });
});
// 驗證 JWT 的受保護路由
app.get("/protected", (req, res) => {
const token = req.headers.authorization?.split(" ")[1];
try {
const decoded = jwt.verify(token, secret);
res.json({ message: "成功訪問", user: decoded });
} catch (error) {
res.status(401).json({ message: "無效的 token" });
}
});
Cookie Session 實例(前端 + 後端簡單會話)
-
前端(無需特別處理 Cookie):
// 登入請求
async function login(username, password) {
const response = await fetch("https://api.example.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
credentials: "include", // 確保發送 Cookie
});
const data = await response.json();
console.log("登入結果:", data);
}
// 取得受保護資料
async function getProtectedData() {
const response = await fetch("https://api.example.com/protected", {
method: "GET",
credentials: "include", // 自動帶上 Cookie
});
const data = await response.json();
console.log("受保護資料:", data);
}
// 使用範例
login("user", "password123");
getProtectedData(); -
後端(Node.js 使用 express-session 管理 Session):
const express = require("express");
const session = require("express-session");
const app = express();
app.use(express.json());
app.use(
session({
secret: "your-session-secret",
resave: false,
saveUninitialized: false,
cookie: { httpOnly: true, secure: true, maxAge: 3600000 }, // 1 小時
})
);
// 登入生成 Session
app.post("/login", (req, res) => {
const { username, password } = req.body;
// 假設驗證成功
req.session.user = { username, role: "user" };
res.json({ message: "登入成功" });
});
// 驗證 Session 的受保護路由
app.get("/protected", (req, res) => {
if (req.session.user) {
res.json({ message: "成功訪問", user: req.session.user });
} else {
res.status(401).json({ message: "未登入" });
}
});
六、總結
-
JWT 適合無狀態、高效能、分散式系統,特別是 API 驅動的應用程式,但需注意 XSS 防護和 token 管理。
-
Cookie Session 適合傳統網頁應用,安全性較高且易於管理,但伺服器需承擔儲存負擔,且跨域處理較複雜。
-
如果你是前端工程師,開發 SPA 或行動應用程式,JWT 是較常見的選擇,因為它與前端框架和 API 整合更方便;如果是傳統網頁應用,Cookie Session 可能更簡單且安全。