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

Node.js与React中bcryptjs密码匹配失败问题求助

问题

使用bcryptjs处理应用密码时,用户注册功能正常,但使用同一密码登录时,bcrypt.compare()始终返回false,提示密码不匹配。已确认邮箱和密码输入正确,数据库中存储的密码哈希格式正常。

相关代码

Node.js 登录端点代码

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const UserModel = require('../models/user_model');

const router = express.Router();
const SECRET_KEY = 'secretKey';

router.post('/login', async (req, res) => {
    const { email, password } = req.body;

    console.log('Login attempt with email:', email);

    try {
        const user = await UserModel.findOne({ email });
        if (!user) {
            console.log('User not found');
            return res.status(400).json({ message: 'Invalid email or password' });
        }

        console.log('Stored user record:', user);

        const isMatch = await bcrypt.compare(password, user.password);
        console.log('Password comparison result:', isMatch);
        if (!isMatch) {
            console.log('Password does not match');
            return res.status(400).json({ message: 'Invalid email or password' });
        }

        const token = jwt.sign({ userId: user._id }, SECRET_KEY);
        res.json({ token });
    } catch (error) {
        console.error('Error logging in:', error);
        res.status(500).json({ error: 'Error logging in' });
    }
});

module.exports = router;

React 登录表单代码

import React, { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const Login = () => {
    const [form, setForm] = useState({ email: '', password: '' });
    const navigate = useNavigate();

    const handleSubmit = async (event) => {
        event.preventDefault();

        try {
            const response = await axios.post('http://localhost:5500/users/login', form, {
                headers: { 'Content-Type': 'application/json' }
            });
            localStorage.setItem('token', response.data.token);
            loadCart(); // 假设存在加载购物车的函数
            navigate('/profile');
        } catch (error) {
            console.error('Error logging in:', error);
            alert('Invalid email or password');
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="email"
                name="email"
                value={form.email}
                onChange={(e) => setForm({ ...form, email: e.target.value })}
                required
            />
            <input
                type="password"
                name="password"
                value={form.password}
                onChange={(e) => setForm({ ...form, password: e.target.value })}
                required
            />
            <button type="submit">Login</button>
        </form>
    );
};

export default Login;

用户 Schema 代码

// 购物车子 schema
const CartItemSchema = new mongoose.Schema({
    productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
    quantity: { type: Number, required: true },
});

const userSchema = new mongoose.Schema({
    fullName: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    cart: [CartItemSchema]
});

// 保存前哈希密码
userSchema.pre('save', function (next) {
    const user = this;
    if (!user.isModified('password')) return next();

    bcrypt.genSalt(10, (err, salt) => {
        if (err) return next(err);
        bcrypt.hash(user.password, salt, (err, hash) => {
            if (err) return next(err);
            user.password = hash;
            next();
        });
    });
});

控制台输出

Login attempt with email: alex.gray@gmail.com
Password comparison result: false
Stored user record: {
  _id: new ObjectId('666bc37b7280ededaca09d72'),
  fullName: 'Alex Gray',
  email: 'alex.gray@gmail.com',
  password: '$2a$10$YJoBVSDWBeJ4o6e47dAR5u0wFSPv.FkLN0qshs0oYDBXQn3aYYK7e',
  cart: [],
  __v: 0
}
Password comparison result: false
Password does not match

排查建议

  • 验证注册时的哈希钩子触发情况:检查注册接口是否使用了save()或create()方法(这两个会触发pre('save')钩子),如果用了updateOne、insertMany这类批量操作,钩子不会执行,可能导致密码未被正确哈希。另外排查是否存在旧数据(比如手动插入的用户),这类数据的密码可能没有经过哈希处理。
  • 核对前端传递的密码:在登录接口中添加console.log('Received password:', req.body.password),确认前端传过来的密码和用户实际输入的完全一致,排除空格、大小写或编码问题。
  • 本地测试哈希一致性:写个简单脚本,用用户实际输入的密码和数据库中存储的哈希值直接对比,看是否返回true:
    const bcrypt = require('bcryptjs');
    const testPassword = '用户输入的真实密码';
    const storedHash = '$2a$10$YJoBVSDWBeJ4o6e47dAR5u0wFSPv.FkLN0qshs0oYDBXQn3aYYK7e';
    
    bcrypt.compare(testPassword, storedHash).then(result => console.log(result));
    
    如果脚本返回false,说明密码确实不匹配;如果返回true,则问题出在登录流程的密码传递环节。
  • 检查密码字段类型:确保Schema中password字段是String类型,避免被意外转换为其他类型(如Number)导致哈希值被篡改。
  • 确认bcryptjs版本一致性:检查后端bcryptjs的版本,确保注册和登录使用的是同一版本,不同版本可能存在哈希算法兼容性问题。
  • 排查重复哈希情况:检查注册接口是否手动对密码进行了哈希,导致Schema钩子再次哈希,最终存储的是“哈希的哈希”,对比自然失败。可以在注册时打印哈希前后的密码值,确认只被哈希一次。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 14:57:32