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 ---") }) // ---------------------------- // // ---------------------------- //
错误截图

解决方案
核心原因
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
相关产品推荐
相关产品推荐

