ExpressJS+Passport登录successRedirect失效及bcrypt.compare始终为false问题
NodeJS登录验证失败:bcrypt.compare始终返回false导致无法跳转首页
我是NodeJS新手,使用MySQL、ExpressJS和Passport搭建了注册/登录页面,但登录成功后未按预期跳转到首页,反而回到登录页(这本是failureRedirect的预期行为)。
更新:我发现passport.js文件中的await bcrypt.compare(password, user.password)始终返回false,导致无法进入成功分支,但不清楚原因。
相关代码
passport.js配置
const LocalStrategy = require("passport-local").Strategy const bcrypt = require("bcrypt") function initialize(passport, getUserByEmail, getUserById) { // Function to authenticate users const authenticateUsers = async(email, password, done) => { // Get users by email const user = await getUserByEmail(email) console.log(user) if (user == null) { return done(null, false, { message: "User is not registered" }) } try { if (await bcrypt.compare(password, user.password)) { return done(null, user) } else { return done(null, false, { message: "Wrong credential(s)" }) } } catch (e) { console.log(e); return done(e) } } passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUsers)) passport.serializeUser((user, done) => done(null, user.id)) passport.deserializeUser((id, done) => { return done(null, getUserById(id)) }) } module.exports = initialize
server.js代码
if (process.env.NODE_ENV !== "production") { require("dotenv").config() } const express = require("express") const db = require("./config/database") const bcrypt = require("bcrypt") const { Prisma } = require("@prisma/client") const initializePassport = require("./config/passport") const flash = require("express-flash") const session = require("express-session") const { application } = require("express") const passport = require("passport") const server = express() initializePassport( passport, async email => await db.user.findFirst({ where: { email } }), async id => await db.user.findFirst({ where: { id } }) ) server.use(express.urlencoded({ extended: false })) //this code is to get the form data in req.body server.use(flash()) server.use(session({ secret: process.env.SESSION_SECRET, resave: false, // we want to resave the session variable if nothing is changed saveUninitialized: false })) server.use(passport.initialize()) server.use(passport.session()) async function main() { const PORT = 8080 server.listen(PORT, function() { console.log(`Server started on port ${PORT}...`) }) } server.get('/', async(req, res) => { res.render("index.ejs") }) server.get('/login', (req, res) => { res.render('login.ejs') }) server.post('/login', passport.authenticate("local", { successRedirect: "/", failureRedirect: "/login", failureFlash: true })) server.get('/registration', (req, res) => { res.render('registration.ejs') }) server.post('/registration', async(req, res) => { // console.log(req.body) const encryptedPassword = await bcrypt.hash(req.body.password.toString(), 10) const { firstName, lastName, email } = req.body console.log(firstName, lastName, email, encryptedPassword) if (email && encryptedPassword) { try { //db.promise().query(`INSERT INTO user (email, password, firstName, lastName) VALUES('${email}','${encryptedPassword}','${firstName}','${lastName}')`) const result = await db.user.create({ data: { email: email, password: encryptedPassword, firstName: firstName, lastName: lastName } }) console.log(result); //res.status(201).send({ message: "User is created" }) res.redirect("/login") } catch (error) { console.log(error) res.redirect("/registration") } finally { await db.$disconnect(); } } }) main();
登录视图页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Login</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> <style> .main { background-color: #EAF7FF; width: 100%; height: 100vh; margin: auto; } .form-container { background-color: rgb(255, 255, 255); max-width: 500px; margin: 0 auto; padding: 30px; border: 1px solid #ccc; border-radius: 10px; box-shadow: 0 0 10px #ccc; } .btn { background-color: #4F95FF; width: 100px; border-radius: 14px; } </style> </head> <body> <div class="main"> <div class="form-container"> <form action="/login" method="POST"> <h2 class="text-center">Login</h2> <div class="form-group"> <input type="email" name="email" class="form-control" id="email" placeholder="Email"> </div> <div class="form-group"> <input type="password" name="password" class="form-control" id="password" placeholder="Password"> </div> <div class="form-group form-check"> <input type="checkbox" class="form-check-input" id="remember-me"> <label class="form-check-label" for="remember-me">Remember me</label> </div> <div class="text-center"> <button type="submit" class="btn btn-primary btn-rounded btn-lg">Login</button> </div> <div class="text-center"> <p>Don't have an account? <a href="registration"> Register</p> </div> </form> </div> </div> </body> </html>
排查方向与解决方法
- 数据库字段长度问题:bcrypt生成的哈希值是60位字符串,检查MySQL中
user表的password字段类型是否为VARCHAR(255)。如果字段长度不足,哈希值会被截断,导致对比失败。修改字段长度后,需要重新注册用户(旧数据的密码已被截断,无法正常验证)。 - 注册过程的密码验证:在注册代码中添加日志,确认原始密码和哈希后的密码是否匹配,再核对数据库中存储的密码是否与日志中的哈希值一致:
如果数据库中的密码和日志不一致,说明存储过程存在问题,检查Prisma的创建语句是否正确。// 注册时临时添加日志 const rawPassword = req.body.password; console.log('原始密码:', rawPassword); const encryptedPassword = await bcrypt.hash(rawPassword, 10); console.log('哈希后密码:', encryptedPassword); - Prisma查询的密码准确性:在passport的
authenticateUsers函数中,打印从数据库查询到的user.password,确认和数据库中存储的密码一致:
如果不一致,检查const user = await getUserByEmail(email) console.log('数据库存储的密码:', user.password);schema.prisma中的User模型定义,确保password字段为String类型,且没有额外的转换逻辑。 - 中间件顺序检查:确保
session中间件在passport.initialize()和passport.session()之前加载,你的当前代码顺序是正确的,若之前调整过顺序需恢复。
内容的提问来源于stack exchange,提问作者Sami
相关产品推荐
相关产品推荐

