用过mongoose的进来看看这是什么Nodejs问题
用过mongoose的进来看看这是什么Nodejs问题
首先我定义一个model:
var UserSchema = new Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true },
email: { type:String, required: true}
});
UserSchema.pre('save', function(next){
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
console.log('user.password :',user.password);
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
console.log('hash:',hash);
console.log('user.password :',user.password);
console.log('1')
next();
});
});
next();
});
module.exports = mongoose.model('User', UserSchema);
然后,我在路由里写:
app.post('/sign',function(req,res){
console.log(req.body);
var username = req.body.user,
password = req.body.passwd,
email = req.body.email;
var newUser = new User({
username: username,
password: password,
email: email
});
newUser.save(function(err){
if(err) throw err;
console.log('2')
return res.redirect('/');
});
});
这样保存之后,在mongodb下查询密码还是明文。这个UserSchema.pre(‘save’,fn)是不是有问题?
console.log出来的是2,1。
我现在知道了是先save后再产生hash。请问我的写法是不是不对?我的理解是save之前先生成 hash,但是实现却不行。
根据你提供的代码片段,问题在于 UserSchema.pre('save', fn)
和 newUser.save()
的执行顺序。你提到 console.log
输出的结果是 2, 1
,这表明 save
方法被调用了两次,并且 bcrypt.hash
还未完成时,save
方法就已经被执行了。
解决方案
我们需要确保在 save
方法调用之前,bcrypt.hash
已经完成。可以通过以下方式修改代码:
- 移除多余的
next()
调用:在bcrypt.hash
回调中已经调用了next()
,所以在bcrypt.genSalt
回调外面再调用next()
是不必要的。 - 确保
save
方法只调用一次:确保bcrypt.hash
完成后才调用newUser.save()
。
以下是修改后的代码示例:
// 引入必要的模块
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// 定义用户模型
var UserSchema = new mongoose.Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true },
email: { type: String, required: true }
});
// 在保存前加密密码
UserSchema.pre('save', function(next){
var user = this;
// 只有在密码被修改时才进行加密
if (!user.isModified('password')) return next();
// 生成盐
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// 加密密码
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// 将明文密码替换为加密后的密码
user.password = hash;
next();
});
});
});
// 导出模型
module.exports = mongoose.model('User', UserSchema);
// 路由处理
app.post('/sign', function(req, res) {
console.log(req.body);
var username = req.body.user,
password = req.body.passwd,
email = req.body.email;
var newUser = new User({
username: username,
password: password,
email: email
});
// 保存新用户
newUser.save(function(err) {
if (err) {
console.error(err);
return res.status(500).send('Error saving user.');
}
console.log('User saved successfully.');
return res.redirect('/');
});
});
解释
- 移除多余的
next()
:在bcrypt.hash
回调内部已经调用了next()
,因此不需要在bcrypt.genSalt
回调外部再次调用next()
。 - 确保
save
只调用一次:确保在所有异步操作(如bcrypt.hash
)完成后才调用newUser.save()
。
通过这些更改,可以确保在 save
操作之前,密码已经被正确地加密并替换为哈希值。
倒数第三行的next去掉试试
是多了一个next,我晕。不知道什么时候打多了一个
从你的描述来看,问题出在 UserSchema.pre('save', fn)
钩子函数中。next()
被调用了两次,一次是在bcrypt操作之前,另一次是在bcrypt操作之后。这会导致即使bcrypt尚未完成其操作,newUser.save()
也会被触发。
正确的做法是只在bcrypt操作完成之后调用 next()
,确保密码已经被哈希处理后再保存用户信息。
示例修正后的代码
var UserSchema = new Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true },
email: { type: String, required: true }
});
UserSchema.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
// Comment out or remove the following line
// next();
});
module.exports = mongoose.model('User', UserSchema);
解释
- 移除多余的
next()
:在bcrypt操作完成后才调用next()
。 - 确保bcrypt操作完成:将所有相关的逻辑放在bcrypt的回调函数中,以确保只有当密码已经成功哈希后才继续执行。
通过这种方式,你可以确保密码在保存到数据库前已经被正确哈希。