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

MERN集成Passport时登录路由返回Cannot POST /login问题

问题描述

在MERN应用中集成Passport后,用户注册及其他路由均正常工作,但调用POST /login路由时,仅在输入正确的用户名/密码时会返回Cannot POST /login(邮箱不存在、密码错误等认证失败场景可正常响应错误信息)。

相关代码

Passport.js

const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");

const initialize = async (passport, getUserByEmail, getUserById) => {
  const authenticateUser = async (email, password, done) => {
    const user = await getUserByEmail(email);
    if (user == null) {
      return done(null, false, {msg: "No user with that email"})
    }

    try {
      console.log(`pass: ${password}, hash: ${user.password}`)
      if (await bcrypt.compare(password, user.password)) {
        console.log(user)
        return done(null, user);
      } else {
        return done(null, false, {msg: "Password incorrect"})
      }
    } catch (e) {
      return done(e)
    }
  }
  passport.use(new LocalStrategy({ usernameField: "email"}, authenticateUser))
  passport.serializeUser((user, done) => done(null, user._id));
  passport.deserializeUser(async (id, done) => done(null, await getUserById(id)));
}

module.exports = initialize

路由代码

if (process.env.NODE_ENV !== "production") {
  require("dotenv").config();
}

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const ItemModel = require('./Models/Item');
const DepartmentModel = require("./Models/Department");
const PeopleModel = require("./Models/Person");
const HouseModel = require('./Models/House');
const ListModel = require('./Models/List');
const bcrypt = require("bcrypt");
const UsersModel = require('./Models/User');
const passport = require("passport");
const flash = require("express-flash");
const session = require("express-session");
const initialize = require("./Passport")
const mongoStore = require('express-session-mongo');

const getUserByEmail = async (email) => {
  return await UsersModel.findOne({email: email});
}

const getUserById = async (id) => {
  return await UsersModel.findById(id);
}


const app = express()

// middleware
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
app.use(flash())
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
}))
app.use(passport.initialize())
app.use(passport.session())


// connect to mongoDB
mongoose.connect(process.env.DB)

initialize(passport, getUserByEmail, getUserById)

... 省略无关代码 ...

// ---------------------------- //
// -----   AUTH ROUTES    ----- //
// ---------------------------- //

app.delete("/logout", (req, res) => {
  req.logout((err) => {
    if (err) { return next(err); }
    res.json({ msg: "Logged out"});
  });
});

// !!!!!!LOGIN ROUTE!!!!!!!
app.post("/login", passport.authenticate("local"))

app.post("/register", async (req, res) => {
  try {
    const hashedPassword = await bcrypt.hash(req.body.password, 10)
    await UsersModel.create({
      username: req.body.username,
      password: hashedPassword,
      email: req.body.email
    })
    res.json({ msg: "Registered new user"})
  } catch {
    res.json({ msg: "Error"})
  }
})

const checkAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }

  res.json({msg: "Not authenticated"});
}

const checkNotAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return res.json({msg: "Authenticated"});
  }

  return next();
}

// ---------------------------- //
// -----   START SERVER   ----- //
// ---------------------------- //

app.listen(process.env.PORT, () => {
  console.log("--- Server is UP and running ---")
})

// ---------------------------- //
// ---------------------------- //

错误截图

认证成功时的404错误

解决方案

核心原因

passport.authenticate("local")在认证成功后,默认会尝试执行重定向操作(比如跳转到首页),但你的后端是纯API服务,没有对应的页面路由,因此触发404错误。而认证失败时,Passport会直接返回错误响应,不会触发重定向,所以能正常返回提示信息。

修复方法

方法1:自定义认证成功后的响应逻辑,禁用重定向

修改登录路由,通过配置或自定义回调,让认证成功后返回JSON响应而非重定向:

// 方式A:通过配置禁用重定向并添加自定义响应
app.post("/login", passport.authenticate("local", {
  failureFlash: true,
  successRedirect: false // 禁用默认重定向
}), (req, res) => {
  // 认证成功后返回用户信息
  res.json({ 
    msg: "登录成功", 
    user: { id: req.user._id, email: req.user.email, username: req.user.username } 
  });
});

// 方式B:使用自定义回调,更灵活控制流程
app.post("/login", (req, res, next) => {
  passport.authenticate("local", (err, user, info) => {
    if (err) return next(err); // 处理服务器错误
    if (!user) return res.json(info); // 认证失败返回错误信息
    // 手动完成登录流程
    req.logIn(user, (err) => {
      if (err) return next(err);
      return res.json({ 
        msg: "登录成功", 
        user: { id: user._id, email: user.email, username: user.username } 
      });
    });
  })(req, res, next);
});

方法2:完善Session配置(可选,生产环境建议)

Passport依赖Session维持用户登录状态,当前配置未指定Session存储,生产环境建议用MongoDB存储Session,避免内存泄漏:

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  // 配置MongoDB存储Session
  store: new mongoStore({
    url: process.env.DB,
    collectionName: 'sessions'
  }),
  // 设置Cookie有效期
  cookie: {
    maxAge: 24 * 60 * 60 * 1000 // 1天
  }
}))

额外注意事项

  • 确保deserializeUser中的getUserById函数能正确查询并返回用户对象,这是Session反序列化的关键。
  • 前端请求登录接口时,需确保携带Cookie(如果使用Session认证),否则登录状态无法维持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 03:04:56