iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0

今日目標說明

昨天我們把朋友的帳號密碼存進資料庫了。今天,當朋友在前端輸入正確的帳密後,後端要怎麼「記住」他已經登入了呢?這一次,我們不使用傳統的 Session Cookie,而是採用現代 Web 開發的主流:JWT (JSON Web Token)

【技術原理解析】Token-based (JWT) vs Session-based (Cookie)

為什麼在前後端分離的架構中,大家更偏愛 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登入成功

Postman登入成功,取得token。

JWT解碼畫面

將真實產生的 JWT 字串,貼到 jwt.io 網站 (https://www.jwt.io/),展示被解碼出 payload 裡面的 usernamerole

小結

我們成功發放了 JWT 數位通行證!但發了通行證,總要有人來負責「驗票」吧?明天,我們要用 Express Middleware (中介軟體) 來打造系統的守衛!


上一篇
DAY 8:實作封閉式系統帳號管理 (不開放註冊的設計)
下一篇
DAY 10:打造 API 防護罩 (Express Middleware 實作)
系列文
就決定是你了!打造寶可夢持有追蹤系統 (React x Express)11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言