Express + MongoDB 实现用户登出
一、简单登出(客户端移除 JWT)
这种方式只需要客户端移除本地存储的 JWT 即可,服务端不需要额外处理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>User Logout</title>
</head>
<body>
<button id="logoutButton">Logout</button>
<script>
const logoutButton = document.getElementById("logoutButton");
logoutButton.addEventListener("click", () => {
// 移除本地存储的 JWT
localStorage.removeItem("jwtToken");
// 可以跳转到登录页面
window.location.href = "/login";
});
</script>
</body>
</html>
二、服务端辅助登出(JWT 黑名单)
为了防止已登出的 JWT 被恶意使用,服务端可以将登出的 JWT 加入黑名单。可以使用 MongoDB 来存储这个黑名单。
1. 创建黑名单模型(`blacklistModel.js`)
const mongoose = require("mongoose");
const blacklistSchema = new mongoose.Schema({
token: {
type: String,
required: true,
unique: true,
},
expiresAt: {
type: Date,
default: Date.now,
expires: "1h", // 设置自动过期时间,与 JWT 过期时间一致
},
});
const Blacklist = mongoose.model("Blacklist", blacklistSchema);
module.exports = Blacklist;
2. 在 `app.js` 中添加登出路由
const express = require("express");
const connectDB = require("./db");
const User = require("./userModel");
const jwt = require("jsonwebtoken");
const Blacklist = require("./blacklistModel");
const app = express();
const port = 3000;
const secretKey = "yourSecretKey";
// 解析请求体中的 JSON 数据
app.use(express.json());
// 连接数据库
connectDB();
// 处理用户登出的路由
app.post("/logout", async (req, res) => {
try {
const token = req.headers["authorization"]?.split(" ")[1];
if (!token) {
return res.status(401).json({ message: "No token provided" });
}
// 验证 JWT
jwt.verify(token, secretKey, async (err, decoded) => {
if (err) {
return res.status(401).json({ message: "Invalid token" });
}
// 将 JWT 加入黑名单
const newBlacklistedToken = new Blacklist({ token });
await newBlacklistedToken.save();
res.json({ message: "Logout successful" });
});
} catch (error) {
console.error("Error during logout:", error);
res.status(500).json({ error: "Internal Server Error" });
}
});
// 验证 JWT 中间件,在需要验证的路由前使用
const verifyToken = (req, res, next) => {
const token = req.headers["authorization"]?.split(" ")[1];
if (!token) {
return res.status(401).json({ message: "No token provided" });
}
// 检查是否在黑名单中
Blacklist.findOne({ token }, (err, blacklistedToken) => {
if (err) {
return res.status(500).json({ error: "Internal Server Error" });
}
if (blacklistedToken) {
return res.status(401).json({ message: "Token has been revoked" });
}
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
return res.status(401).json({ message: "Invalid token" });
}
req.user = decoded;
next();
});
});
};
// 示例受保护的路由
app.get("/protected", verifyToken, (req, res) => {
res.json({ message: "This is a protected route", user: req.user });
});
// 启动 Express 服务器
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});