今天是鐵人賽 Day16,目標是完成會員登入 API,導入 JWT 驗證,並加入 admin 權限控制,確保只有登入使用者可存取受保護 API,管理者才可刪除會員。
1️⃣ JWT 驗證中介層(Auth Middleware)
使用者呼叫受保護 API 時,需要在 Header 帶入 Authorization: Bearer 
中介層會驗證 token 是否有效,並將使用者資訊放到 req.user,方便後續使用
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ message: "未授權,請登入" });
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch {
    return res.status(401).json({ message: "Token 無效或已過期" });
  }
};

2️⃣ Admin 權限中介層(Admin Middleware)
刪除會員等敏感操作,僅 admin 可執行
非 admin 嘗試操作會回傳「權限不足」
const adminMiddleware = (req, res, next) => {
  if (req.user.role !== "admin") {
    return res.status(403).json({ message: "權限不足,僅限管理者操作" });
  }
  next();
};

3️⃣ 會員登入 API(POST /login)
使用者輸入 email + 密碼
密碼比對 bcrypt,成功後產生 JWT token
回傳使用者基本資料與 token(不回傳密碼)
router.post("/login", async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email: email.trim().toLowerCase() });
  const isMatch = await bcrypt.compare(password, user.password);
  const token = jwt.sign(
    { id: user._id, username: user.username, role: user.role },
    JWT_SECRET,
    { expiresIn: "1d" }
  );
  res.json({
    message: "登入成功",
    user: { id: user._id, username: user.username, email: user.email, role: user.role },
    token
  });
});

4️⃣ 受保護 API 範例
取得所有會員 / 單一會員需登入
刪除會員需登入且為 admin
router.get("/", authMiddleware, async (req, res) => { ... });
router.delete("/:id", authMiddleware, adminMiddleware, async (req, res) => { ... });

5️⃣ 後端測試流程(Postman)
POST /api/users/login → 登入會員,取得 token
GET /api/users → 在 Header 加入 Authorization: Bearer  測試取得會員
DELETE /api/users/:id → 需 admin 權限,非 admin 會回傳「權限不足」
🐛 遇到的問題與解決
token 過期或缺失 → 新增驗證中介層,回傳提示訊息
刪除會員非 admin 嘗試 → 新增 adminMiddleware,回傳權限不足
💡 Day16 收穫