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

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>

问题根源

  1. 表单字段名拼写错误:login.ejs中邮箱输入框name属性为usarname,而Passport Local默认读取username字段,导致无法获取邮箱值,验证失败。
  2. 策略参数拼写错误:Passport Local策略的回调参数写成usarname,与表单字段错误匹配,无法正确查询数据库用户。
  3. 序列化逻辑不规范:直接序列化整个用户对象到会话,可能引发存储或反序列化问题,标准做法是仅序列化用户ID。
  4. 登录失败无反馈:原登录路由仅重定向,未传递错误信息到前端,用户无法知晓失败原因。

修复方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 04:15:57