iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Modern Web

用 LINE OA 打造中小企業訂單系統:從零開始的 30 天實作紀錄系列 第 18

讓登入更安全:JWT 與 Session 的導入

  • 分享至 

  • xImage
  •  

昨天我們建立了 Admin 後台雛形與登入頁面,但目前的登入流程仍是假裝的(輸入什麼都能進 Dashboard)。

今天要導入真正的 身分驗證機制,利用 JWT(JSON Web Token)Session,確保只有合法的管理員能登入後台。

我們會示範如何用後端驗證帳密,簽發 JWT,並在前端存取與帶入,完成基本的安全登入流程。


為什麼需要身分驗證?

  • 避免任何人都能直接打 API。

  • 確保只有管理員能操作訂單。

  • 是後台系統的基本安全需求。


JWT vs Session

  • Session:伺服器維護狀態,透過 Cookie 綁定。

  • JWT:Stateless,前端帶 Token,後端用密鑰驗證即可。

  • 我們的系統更偏 API 架構 → 採用 JWT


登入流程設計

  1. 管理員在前端輸入帳密 → 呼叫 /auth/admin/login

  2. 後端驗證帳密 → 簽發 JWT。

  3. 前端把 JWT 存在 LocalStorage。

  4. 呼叫 API(如 /orders)時加上 Authorization: Bearer <JWT>


安裝套件

npm i bcrypt jsonwebtoken cors

bcrypt
用途:安全地雜湊密碼(不可逆),登入時用「明碼 + 同樣的鹽」比對。
為什麼:不能把明碼或可逆加密儲存在 DB;bcrypt 內建鹽與延遲計算,能降低爆破成功率。

jsonwebtoken (JWT)
用途:簽發/驗證 JSON Web Token,讓前端帶著 Token 打 API,後端驗證簽章與時效。
為什麼:Stateless 驗證,適合 API 架構與多服務。

cors
用途:設定 跨來源資源共享,讓你的 Express API 可以被不同「來源(協定+網域+埠)」的前端呼叫。
為什麼:瀏覽器有同源政策;Vite 5173 要打 Express 3000,必須讓後端回應 CORS 標頭。


程式碼實作

後端 - 建立 src/routes/auth.js

// src/routes/auth.js
const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const router = express.Router();

// (示範)硬編一個管理員帳號;正式應該放 DB
// 密碼:admin123
const ADMIN_EMAIL = process.env.ADMIN_EMAIL || "admin@test.com";
const ADMIN_HASH  = process.env.ADMIN_HASH  || "$2b$10$2wqv7lq7i0v1u5cN3N5V7e2Qm4qVw9F3r1b0yC5XxYVQm0q8qA0we";

router.post("/login", async (req, res) => {
  const { email, password } = req.body || {};
  if (!email || !password) return res.status(400).json({ message: "缺少 email 或 password" });

  if (email !== ADMIN_EMAIL) return res.status(401).json({ message: "帳號不存在" });

  const ok = await bcrypt.compare(password, ADMIN_HASH);
  if (!ok) return res.status(401).json({ message: "密碼錯誤" });

  const token = jwt.sign(
    { role: "admin", email },
    process.env.JWT_SECRET || "dev_secret_change_me",
    { expiresIn: "7d" }
  );
  res.json({ jwt: token });
});

// 共用:保護管理員 API 的 middleware
function authAdmin(req, res, next) {
  const auth = req.headers.authorization || "";
  const token = auth.startsWith("Bearer ") ? auth.slice(7) : null;
  if (!token) return res.status(401).json({ message: "缺少 JWT" });

  jwt.verify(token, process.env.JWT_SECRET || "dev_secret_change_me", (err, decoded) => {
    if (err || decoded.role !== "admin") return res.status(403).json({ message: "驗證失敗" });
    req.admin = decoded;
    next();
  });
}

module.exports = { router, authAdmin };

想用資料庫:可以改成 Admin model(admin.model.js)查 email 取 passwordHash,其餘不變。

後端 - 在 src/index.js 掛上路由

// src/index.js
const express = require("express");
const path = require("path");
const cors = require("cors");
const app = express();

app.use(cors({ origin: true, credentials: true }));
app.use(express.json());

// 提供 LIFF 靜態檔
app.use(express.static(path.resolve(__dirname, "..", "liff-form")));

// 你的既有訂單路由
app.use("/orders", require("./routes/order"));

// 這行是關鍵:把 Day18 的登入路由掛到 /auth/admin
const { router: authRouter } = require("./routes/auth");
app.use("/auth/admin", authRouter);

app.listen(3000, () => console.log("API on http://localhost:3000"));

前端 Login.jsx

async function handleLogin(e) {
  e.preventDefault();
  try {
    const res = await axios.post("http://localhost:3000/auth/admin/login", {
      email, password
    });
    localStorage.setItem("jwt", res.data.jwt);
    window.location.href = "/dashboard";
  } catch (err) {
    alert("登入失敗");
  }
}

前端呼叫受保護 API

const token = localStorage.getItem("jwt");
const res = await axios.get("http://localhost:3000/orders", {
  headers: { Authorization: `Bearer ${token}` }
});

前端畫面 Demo

https://ithelp.ithome.com.tw/upload/images/20251002/201788681vOKonHiRa.png

從圖片中可以發現,照上述做法操作完成後,會遇到典型的 CORS 問題,明天(Day 19)會詳細向大家說明這個問題原因並且提供解法!
https://ithelp.ithome.com.tw/upload/images/20251002/20178868hlLPrQlk6j.png


重點回顧

  • Admin 後台不能只是「畫面」,必須加上 身分驗證

  • JWT 適合 API 架構,讓我們能保護 /orders 等敏感路由。

明天(Day 19)我們修正完 CORS 問題後,會讓後台 串接 API,顯示訂單列表,讓管理員能真正操作訂單。


Day 17 修正

昨日把 Router 架構要放的位置打成 Main.jsx 了,應該要放在 App.jsx!
https://ithelp.ithome.com.tw/upload/images/20251002/20178868kd8xp4L34D.png


上一篇
店家需要的後台:Admin 管理介面雛形
下一篇
跨域的絆腳石:CORS 問題與後台 API 串接
系列文
用 LINE OA 打造中小企業訂單系統:從零開始的 30 天實作紀錄22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言