Express + MongoDB 实现用户登出

发布于:2025-03-03 ⋅ 阅读:(118) ⋅ 点赞:(0)

一、简单登出(客户端移除 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}`);

});


网站公告

今日签到

点亮在社区的每一天
去签到