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

使用bcrypt验证哈希密码时出现TypeError及登录后页面空白问题

问题分析与解决方案

1. 登录后页面空白+Header charAt 错误的核心原因

你的signin路由中,bcrypt.compare的回调参数名和外层的用户变量重名了,导致密码验证通过后,你拿到的result是布尔值而非用户对象:

// 外层定义了用户变量result
const result = user; 

bcrypt.compare(password, result.password, function(err, result) {
    // 这里的result是bcrypt返回的匹配结果(true/false),覆盖了外层的用户对象
    if (!result) { 
        return res.status(400).json({ message: "Invalid credentials" });  
    } else {
        // 这里的result是true,根本没有email和_id属性,生成的JWT无效,返回的result也是true
        const token = jwt.sign({ email: result.email, id: result._id }, secret, { expiresIn: "1h" }); 
        return res.status(200).json({ result, token })
    }
})

前端拿到result: true后,存在localStorage的profile里,user.result是布尔值,没有name属性,所以user?.result.name是undefined,调用charAt(0)就会抛出类型错误,同时错误的用户数据导致页面渲染异常空白。

修复signin路由:修改回调参数名,避免变量覆盖:

router.post("/signin", (req, res) => {
    const password = req.body.password;  // 直接用req.body即可,无需res.req.body
    
    User.findOne({ email: req.body.email }).then(user => {
        if(!user) {
            return res.status(404).json({ email: "email does not exist." }); 
        }
        // 修改回调参数名为isMatch,避免覆盖user变量
        bcrypt.compare(password, user.password, function(err, isMatch) {
            if (err) throw err;
            if (!isMatch) { 
                return res.status(400).json({ message: "Invalid credentials" });  
            } else {
                const token = jwt.sign({ email: user.email, id: user._id }, secret, { expiresIn: "1h" }); 
                return res.status(200).json({ result: user, token })
            }
        })
    }).catch(err => console.log(err)); // 捕获数据库查询错误
});

2. 修复Header组件的charAt安全问题

即便用户数据正确,也要处理name为空的情况,给charAt加上可选链或者默认值:

<Avatar alt={user?.result.name || 'User'} src={user?.result.imageUrl}>
    {user?.result.name?.charAt(0) || 'U'}
</Avatar>

同时优化useEffect逻辑,避免重复解析localStorage:

useEffect(() => {
    // 监听location变化时,重新读取localStorage更新user状态
    const profile = JSON.parse(localStorage.getItem('profile'));
    setUser(profile);
}, [location]);

3. 修复signup路由的异步响应错误

你的signup路由中,在bcrypt哈希密码的异步操作外直接调用了res.status(200).json({ result, token });,这会导致重复响应(后面result.save()还会调用res.json(user)),而且此时密码还没哈希,返回的result是未保存的用户对象,数据不正确。

修复signup路由:将响应逻辑移到异步操作内部:

router.post("/signup", (req, res) => {
    const { password, confirmPassword, email, firstName, lastName } = req.body;  // 解构赋值更简洁

    User.findOne({ email })
        .then(user => {
            if(user) {
                return res.status(400).json({ email: "email already exists" }); 
            } 
            if(password !== confirmPassword){
                return res.status(400).json({ message: "Password don't match."});
            } 

            const newUser = new User({ 
                name: `${firstName} ${lastName}`, 
                email, 
                password
                // 不要提前存token到数据库,token在登录时生成即可
            }); 

            bcrypt.genSalt(10, (err, salt) => {
                if (err) throw err;
                bcrypt.hash(password, salt, (err, hash) => {
                    if (err) throw err; 
                    newUser.password = hash; 
                    newUser.save()
                        .then(savedUser => {
                            // 保存成功后再生成token并返回
                            const token = jwt.sign({ email: savedUser.email, id: savedUser._id}, 'test', { expiresIn: "1h" }); 
                            res.status(200).json({ result: savedUser, token }); 
                        })
                        .catch(err => {
                            console.log(err);
                            res.status(500).json({ message: "Failed to create user" });
                        }); 
                }); 
            }); 
        })
        .catch(err => {
            console.log(err);
            res.status(500).json({ message: "Database query error" });
        }); 
});

额外建议

  • 不要把JWT密钥硬编码在代码里,用环境变量存储(比如process.env.JWT_SECRET)
  • 数据库操作尽量用async/await替代回调,代码更易读和维护
  • 前端登录成功后,确保正确将后端返回的{ result, token }存入localStorage,比如:localStorage.setItem('profile', JSON.stringify({ result, token }))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 20:40:30