Passport Local登录异常求助:验证正确仍重定向至登录页
Passport Local 登录后持续重定向至登录页问题排查与修复
问题描述
我是Passport新手,在程序中添加Local Passport后无法登录。登录页面加载后始终重定向到自身,即便登录信息正确也无法进入主页,但注册功能运行正常。
我的Node.js代码
import express from "express"; import bodyParser from "body-parser"; import pg from "pg"; import bcrypt from "bcrypt"; import session from "express-session"; import passport from "passport"; import { Strategy } from "passport-local"; const port = 3000; const app = express(); const saltRounds = 10; app.use(session({ secret: "TOPSECRET", resave: false, saveUninitialized: true, })); app.use(bodyParser.urlencoded({extended: true})); app.use(express.static("public")); app.use(passport.initialize()); app.use(passport.session()); const db = new pg.Client({ user: "postgres", host: "localhost", database: "connectwave", password: "secret", port: 5432, }) db.connect(); let currentId; app.get("/login", (req, res) => { res.render("login.ejs"); }) app.get("/register", (req, res) => { res.render("register.ejs"); }) app.get("/home", async (req, res) => { console.log(req.user); if (req.isAuthenticated()) { const result = await db.query("SELECT * FROM posts ORDER BY id desc"); const data = result.rows; res.render("index.ejs", { data: data, }); } else { res.redirect("/login"); } }) app.post("/login", passport.authenticate('local', { successRedirect: "/home", failureRedirect: "/login", })); app.post("/register", async (req, res) => { const email = req.body.emailRegister; const password = req.body.passwordRegister; try { const checkEmail = await db.query("SELECT * FROM users WHERE email = $1", [email]); if (checkEmail.rows.length > 0) { res.render("register.ejs", { emailMessage: "Email has already been taken.", }); } else { bcrypt.hash(password, saltRounds, async (err, hash) => { if (err) { console.log("Error hashing password", err); } else { const result = await db.query("INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *", [email, hash]); const user = result.rows[0]; currentId = result.rows[0].id; req.login(user, (err) => { if (err) { console.log(err); } else { res.redirect("/home"); } }) } }); } } catch (err) { console.log(err); } }); app.post("/post", async (req, res) => { const post = req.body.createpost; currentId = currentId; try { await db.query("INSERT INTO posts (text, user_id) VALUES ($1, $2)", [post, currentId]) res.redirect("/home"); } catch (err) { console.log(err); } }) app.post("/text", async (req, res) => { const text = req.body.text_post; const id = req.body.checkBtn; try { await db.query("UPDATE posts SET text = $1 WHERE id = $2", [text, id]); res.redirect("/home"); } catch (err) { console.log(err); } }) app.post("/delete", async (req, res) => { const id = req.body.post_id; try { await db.query("DELETE FROM posts WHERE id = $1",[id]); res.redirect("/home"); } catch (err) { console.log(err); } }) passport.use(new Strategy(async function verify(usarname, password, cb) { try { const result = await db.query("SELECT * FROM users WHERE email = $1", [usarname]); if (result.rows.length > 0) { const user = result.rows[0]; const hashPassword = user.password; bcrypt.compare(password, hashPassword, (err, valid) => { if (err) { console.log("Error comparing password", err); return cb(err); } else { if (valid) { currentId = user.id; return cb(null, user); } else { return cb(null, false); } } }); } else { return cb("User not found"); } } catch (err) { console.log(err); } })); passport.serializeUser((user, cb) => { cb(null, user); }); passport.deserializeUser((user, cb) => { cb(null, user); }) app.listen(port, () => { console.log(`Server is running on port ${port}`); });
我的login.ejs页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Connect Wave</title> <link rel="stylesheet" href="/styles/login.css"> <link rel="shortcut icon" href="/images/favicon.ico" type="image/x-icon"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/remixicon@4.1.0/fonts/remixicon.css" rel="stylesheet"> </head> <body> <div class="container-login"> <div class="form-login"> <h1 class="login-title">Sign in to CW</h1> <form action="/login" method="POST"> <div class="input-required"> <input class="email" type="email" name="usarname" placeholder="Email" required> <div class="password-box"> <input class="password" type="password" name="password" placeholder="Password" autocomplete="off" required> <i class="ri-eye-off-line login__eye" id="input-icon"></i> </div> </div> <input type="submit" value="Log in" class="login-btn"> </form> <button class="forgot">Forgot password?</button> <div> <p class="signup">Don't have an account? <span class="signup-colored"><a href="/register">Sign up</a></span></p> </div> </div> </div> <% if (locals.notPasswordMessage) {%> <div class="notPasswordMessage"> <p><%=notPasswordMessage %></p> </div> <% } else if (locals.notEmailMessage) {%> <div class="notEmailMessage"> <p><%=notEmailMessage %></p> </div> <% } %> <script> const emailMessage = document.querySelector(".notEmailMessage") const passwordMessage = document.querySelector(".notPasswordMessage") const showHiddenPassword = (inputPass, inputIcon) => { const input = document.querySelector(inputPass); const iconEye = document.getElementById(inputIcon); iconEye.addEventListener("click", () => { if (input.type === "password") { input.type = "text"; iconEye.classList.remove("ri-eye-off-line"); iconEye.classList.add("ri-eye-line"); } else { input.type = "password"; iconEye.classList.remove("ri-eye-line"); iconEye.classList.add("ri-eye-off-line"); } }); } showHiddenPassword(".password", "input-icon"); setTimeout(function () { emailMessage.classList.add("hidden"); }, 4000); setTimeout(function () { passwordMessage.classList.add("hidden"); }, 4000); </script> </body> </html>
问题根源
- 表单字段名拼写错误:login.ejs中邮箱输入框
name属性为usarname,而Passport Local默认读取username字段,导致无法获取邮箱值,验证失败。 - 策略参数拼写错误:Passport Local策略的回调参数写成
usarname,与表单字段错误匹配,无法正确查询数据库用户。 - 序列化逻辑不规范:直接序列化整个用户对象到会话,可能引发存储或反序列化问题,标准做法是仅序列化用户ID。
- 登录失败无反馈:原登录路由仅重定向,未传递错误信息到前端,用户无法知晓失败原因。
修复方案
1. 修正login.ejs表单字段名
将邮箱输入框的name="usarname"改为name="username":
<input class="email" type="email" name="username" placeholder="Email" required>
2. 修正Passport策略参数拼写
将策略中的usarname改为username:
passport.use(new Strategy(async function verify(username, password, cb) { try { const result = await db.query("SELECT * FROM users WHERE email = $1", [username]); // 其余代码不变 } catch (err) { console.log(err); return cb(err); } }));
3. 规范序列化与反序列化
仅序列化用户ID,反序列化时通过ID查询用户:
passport.serializeUser((user, cb) => { cb(null, user.id); }); passport.deserializeUser(async (id, cb) => { try { const result = await db.query("SELECT * FROM users WHERE id = $1", [id]); if (result.rows.length > 0) { cb(null, result.rows[0]); } else { cb(new Error("用户不存在")); } } catch (err) { cb(err); } });
4. 优化登录路由,添加错误反馈
替换原登录路由,自定义验证回调以传递错误信息:
app.post("/login", (req, res, next) => { passport.authenticate('local', (err, user, info) => { if (err) { return next(err); } if (!user) { return res.render("login.ejs", { notPasswordMessage: "邮箱或密码错误" }); } req.login(user, (err) => { if (err) { return next(err); } currentId = user.id; return res.redirect("/home"); }); })(req, res, next); });
5. 注册路由优化(可选)
将注册时的currentId赋值改为直接使用user.id:
currentId = user.id; // 替换原代码中的 currentId = result.rows[0].id;
验证修复
重启服务器后,使用正确的邮箱和密码登录,将成功跳转至/home页面;登录失败时,前端会显示错误提示。
内容的提问来源于stack exchange,提问作者Marcos Neto
相关产品推荐
相关产品推荐

