You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js+MongoDB实现用户名密码认证及Cookie存储问题求助

问题解决:Express + MongoDB 用户登录认证与Cookie存储

问题背景

基于Node.js Express框架和MongoDB开发登录页面,注册功能正常,但登录功能无法正确完成用户认证,需要实现基于MongoDB的用户名/密码验证,并完善用户Cookie存储逻辑。

原代码核心问题

  1. 登录接口仅查询用户存在性,未验证密码正确性,无论匹配结果如何都直接跳转
  2. 用户密码明文存储,存在安全风险
  3. Cookie的secure属性在HTTP开发环境下无法生效,导致Cookie无法设置
  4. 数据库连接未添加错误处理,注册时异步保存操作未等待完成就跳转

修复后的完整代码

//------------modules used-------------//
const express = require("express");
const path = require("path");
const helmet = require("helmet");
const cookieparser = require("cookie-parser");
const mongoose = require("mongoose");
const bcrypt = require("bcrypt"); // 新增:用于密码哈希
//------------modules used-------------//

const app = express();
app.use(helmet());
app.use(cookieparser());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

//-------------------mongodb-----------------//
// 新增数据库连接错误处理
mongoose.connect("mongodb://localhost:27017/loginDB", { useNewUrlParser: true })
  .then(() => console.log("MongoDB连接成功"))
  .catch(err => console.error("MongoDB连接失败:", err));

const userSchema = new mongoose.Schema({
  email: { type: String, unique: true, required: true }, // 新增唯一性约束
  pass: { type: String, required: true },
});
// 新增:保存前自动哈希密码
userSchema.pre("save", async function(next) {
  if (!this.isModified("pass")) return next();
  this.pass = await bcrypt.hash(this.pass, 10);
  next();
});

const User = mongoose.model("User", userSchema);
//-------------------mongodb-----------------//

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

app.get("/", (req, res) => {
  let username = req.cookies.username;
  if (username) {
    return res.render("mainpage", { username });
  } else {
    res.redirect("/login");
  }
});

app.get("/mainpage", (req, res) => {
  let username = req.cookies.username;
  if (username) {
    return res.render("mainpage", { username });
  } else {
    res.redirect("/login");
  }
});

app.get("/register", (req, res) => {
  return res.render("signup");
});

app.get("/login", (req, res) => {
  let bad_auth = req.query.msg ? true : false;
  if (bad_auth) {
    return res.render("login", { error: "Invalid username or password" });
  } else {
    return res.render("login");
  }
});

// 修复登录接口:正确验证用户名和密码
app.post("/login", async (req, res) => {
  try {
    const { username, password } = req.body;
    // 查找用户,用findOne而非find(find返回数组)
    const user = await User.findOne({ email: username });
    if (!user) {
      return res.redirect("/login?msg=fail");
    }
    // 验证密码哈希
    const isPasswordValid = await bcrypt.compare(password, user.pass);
    if (!isPasswordValid) {
      return res.redirect("/login?msg=fail");
    }
    // 根据环境设置secure属性:生产环境HTTPS设为true,开发环境设为false
    const isProduction = process.env.NODE_ENV === "production";
    res.cookie("username", username, {
      maxAge: 30 * 24 * 60 * 60 * 1000,
      secure: isProduction,
      httpOnly: true,
      sameSite: 'lax'
    });
    res.redirect("/mainpage");
  } catch (err) {
    console.error("登录错误:", err);
    res.redirect("/login?msg=fail");
  }
});

// 修复注册接口:等待保存完成再跳转,处理重复注册
app.post("/register", async (req, res) => {
  try {
    const { given_username, given_password } = req.body;
    // 检查用户是否已存在
    const existingUser = await User.findOne({ email: given_username });
    if (existingUser) {
      // 可添加提示用户已存在的逻辑
      return res.redirect("/register");
    }
    const newUser = new User({
      email: given_username,
      pass: given_password,
    });
    await newUser.save(); // 等待保存完成
    const isProduction = process.env.NODE_ENV === "production";
    res.cookie("username", given_username, {
      maxAge: 30 * 24 * 60 * 60 * 1000,
      secure: isProduction,
      httpOnly: true,
      sameSite: 'lax'
    });
    res.redirect("/");
  } catch (err) {
    console.error("注册错误:", err);
    res.redirect("/register");
  }
});

app.get("/logout", (req, res) => {
  res.clearCookie("username");
  return res.redirect("/login");
});

app.listen(3000, () => console.log(`服务器启动在端口3000`));

关键修改说明

  • 密码安全:引入bcrypt库,在用户保存前自动哈希密码,登录时验证哈希值,避免明文存储风险
  • 登录认证逻辑:使用User.findOne查找单个用户,对比密码哈希,只有验证通过才设置Cookie跳转
  • Cookie适配:根据运行环境设置secure属性,开发环境(HTTP)设为false确保Cookie正常生效
  • 异步操作处理:注册和登录接口改用async/await处理MongoDB异步操作,确保数据操作完成后再响应
  • 数据库优化:添加用户邮箱唯一性约束,避免重复注册;新增数据库连接错误处理,便于排查问题
  • 错误处理:添加全局异常捕获,登录/注册失败时统一跳转至对应页面

内容的提问来源于stack exchange,提问作者Arsh Meharwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 00:01:07