昨天我們把朋友的帳號密碼存進資料庫了。今天,當朋友在前端輸入正確的帳密後,後端要怎麼「記住」他已經登入了呢?這一次,我們不使用傳統的 Session Cookie,而是採用現代 Web 開發的主流:JWT (JSON Web Token)。
為什麼在前後端分離的架構中,大家更偏愛 JWT?
| 比較項目 | Session-based (傳統方式) | Token-based / JWT (本專案採用) |
|---|---|---|
| 狀態管理 | Stateful (有狀態):後端伺服器必須在記憶體或 Redis 中記住每一個發出去的 Session ID。 | Stateless (無狀態):後端不記任何東西!JWT 本身就包含了身分資訊與簽章,後端只要「驗證簽章」就知道真偽。 |
| 跨網域 (CORS) | 非常麻煩。遇到前後端不同網域 (例如前端 Render,後端 Vercel),Cookie 會被瀏覽器嚴格阻擋。 | 非常簡單。Token 通常放在 HTTP Header (Authorization: Bearer xxx) 裡面自己帶過去,不受跨網域 Cookie 限制。 |
| 伺服器負載 | 如果用 Serverless,因為實體會一直重新啟動,記憶體中的 Session 會不斷流失。 | 完美契合 Serverless!無狀態的特性讓 API 無論在哪台機器上執行,都能驗證。 |
在 src/routes/auth.ts 中,我們實作登入邏輯並核發 JWT:
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
router.post('/login', async (req, res) => {
const { username, password } = req.body;
// 1. 去資料庫找使用者
const result = await db.execute({
sql: 'SELECT * FROM system_users WHERE username = ?',
args: [username]
});
const user = result.rows[0];
// 2. 比對密碼雜湊
if (!user || !(await bcrypt.compare(password, user.password_hash as string))) {
return res.status(401).json({ error: '帳號或密碼錯誤' });
}
// 3. 密碼正確,簽發 JWT 通行證!
// payload: 要包進去的不敏感資訊 (如 username, role)
// secret: 只有後端知道的密碼 (用來防偽造)
const token = jwt.sign(
{ userId: user.user_id, username: user.username, role: user.role },
process.env.JWT_SECRET as string,
{ expiresIn: '7d' } // 設定 7 天後過期
);
res.json({ token, user: { username: user.username, role: user.role } });
});

Postman登入成功,取得token。

將真實產生的 JWT 字串,貼到 jwt.io 網站 (https://www.jwt.io/),展示被解碼出 payload 裡面的
username與role。
我們成功發放了 JWT 數位通行證!但發了通行證,總要有人來負責「驗票」吧?明天,我們要用 Express Middleware (中介軟體) 來打造系統的守衛!