今天是鐵人賽 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 收穫