无论密码如何,Bcrypt-NodeJS compare()都返回false

时间:2017-09-03 11:25:28

标签: node.js mongoose bcrypt

我知道问题已被问过几次(例如hereherethere,甚至Github,但答案中没有一个真正奏效对我来说......

我正在尝试使用Mongoose和Passport为NodeJS应用程序开发身份验证,并使用Bcrypt-NodeJS来散列用户的密码。

在我决定重构用户架构并使用bcrypt的异步方法之前,一切正常运行没有任何问题。在创建新用户时,散列仍然有效,但我现在无法根据MongoDB中存储的散列验证密码。

我知道什么?

    无论密码是否正确,
  1. protocol DataModelProtocol { typealias ObjectProtocol = NSManagedObject ... } 总是返回bcrypt.compare(),无论密码是什么(我尝试了几个字符串)。
  2. 在用户创建时,密码只进行一次哈希处理(因此哈希不会被重新哈希)。
  3. 给比较方法的密码和哈希是正确的,顺序正确。
  4. 密码和哈希的类型为“String”。
  5. 存储在数据库中时,哈希不会被截断(长度为60个字符串)。
  6. 数据库中提取的哈希值与用户创建时存储的哈希值相同。
  7. 代码

    用户架构

    有些字段已被剥离以保持清晰,但我保留了相关部分。

    false

    密码哈希

    var userSchema = mongoose.Schema({
    
        // Local authentication
        password: {
            hash: {
                type: String,
                select: false
            },
            modified: {
                type: Date,
                default: Date.now
            }
        },
    
        // User data
        profile: {
            email: {
                type: String,
                required: true,
                unique: true
            }
        },
    
        // Dates
        lastSignedIn: {
            type: Date,
            default: Date.now
        }
    });
    

    密码比较

    userSchema.statics.hashPassword = function(password, callback) {
        bcrypt.hash(password, bcrypt.genSaltSync(12), null, function(err, hash) {
            if (err) return callback(err);
            callback(null, hash);
        });
    }
    

    用户身份验证

    userSchema.methods.comparePassword = function(password, callback) {
        // Here, `password` is the string entered in the login form
        // and `this.password.hash` is the hash stored in the database
        // No problem so far
        bcrypt.compare(password, this.password.hash, function(err, match) {
            // Here, `err == null` and `match == false` whatever the password
            if (err) return callback(err);
            callback(null, match);
        });
    }
    

    这可能是我犯的一个“简单”的错误,但我在几个小时内找不到任何错误......如果您有任何想法让这种方法有效,我会很高兴看到它。< / p>

    谢谢你们。

    编辑:

    运行这段代码时,匹配实际上等于userSchema.statics.authenticate = function(email, password, callback) { this.findOne({ 'profile.email': email }) .select('+password.hash') .exec(function(err, user) { if (err) return callback(err); if (!user) return callback(null, false); user.comparePassword(password, function(err, match) { // Here, `err == null` and `match == false` if (err) return callback(err); if (!match) return callback(null, false); // Update the user user.lastSignedIn = Date.now(); user.save(function(err) { if (err) return callback(err); user.password.hash = undefined; callback(null, user); }); }); }); } 。所以我知道我的方法是正确的。我怀疑这与数据库中哈希的存储有关,但我真的不知道是什么原因导致这个错误发生。

    true

    编辑2(和解决方案):

    我把它放在那里以防有一天它对某人有帮助......

    我在我的代码中发现了错误,这是在用户注册期间发生的(实际上是我没有在这里发布的唯一一段代码)。我正在查询var pwd = 'TestingPwd01!'; mongoose.model('User').hashPassword(pwd, function(err, hash) { console.log('Password: ' + pwd); console.log('Hash: ' + hash); user.password.hash = hash; user.comparePassword(pwd, function(err, match) { console.log('Match: ' + match); }); }); 对象而不是user.password ...

    只有通过将我的依赖关系从“brcypt-nodejs”更改为“bcryptjs”才能找到错误,因为bcryptjs在被要求散列对象时抛出错误,而brcypt-nodejs只是散列对象,就好像它一样是一个字符串。

5 个答案:

答案 0 :(得分:0)

bcrypt.hash()有3个参数......出于某种原因你有4个。

而不是

bcrypt.hash(password, bcrypt.genSaltSync(12), null, function(err, hash) {

应该是

bcrypt.hash(password, bcrypt.genSaltSync(12), function(err, hash) {

由于您仅在用户创建期间进行哈希处理,因此您可能无法正确进行哈希处理。您可能需要重新创建用户。

答案 1 :(得分:0)

我知道已经找到了解决方案,但以防万一您从谷歌搜索登陆这里并遇到同样的问题,特别是如果您使用的是schema.pre(&#34; save&#34;)功能,有时候存在多次保存相同模型的趋势,因此每次重新散列密码。如果您使用mongoDB中的引用来创建架构关系,则尤其如此。这是我的注册功能:

注册码

User.create(newUser, (err, user) => {
            if (err || !user) {
                console.warn("Error at stage 1");
                return res.json(transformedApiRes(err, "Signup error", false)).status(400);
            }
            let personData: PersonInterface = <PersonInterface>{};
            personData.firstName = req.body.first_name;
            personData.lastName = req.body.last_name;
            personData.user = user._id;
            Person.create(personData, function (err1: Error, person: any): any {
                if (err1 || !person) {
                    return res.json(transformedApiRes(err1, "Error while saving to Persons", false));
                }
                /* One-to-One relationship */
                user.person = person;
                user.save(function (err, user) {
                    if (err || !user) {
                        return res.json({error: err}, "Error while linking user and person models", false);
                    }
                    emitter.emit("userRegistered", user);
                    return res.json(transformedApiRes(user, `Signup Successful`, true));
                });
            });
        });

正如您所看到的,在User上存在嵌套保存,因为我必须将User模型与Person模型(一对一)链接起来。因此,我遇到了不匹配错误,因为我使用的是预保存函数,每次触发User.create或User.save时,都会调用该函数并重新散列现有密码。预保存中的控制台语句给了我以下内容,表明密码确实已经重新散列:

单次注册后的控制台调试

{ plain: 'passwd',
  hash: '$2b$10$S2g9jIcmjGxE0aT1ASd6lujHqT87kijqXTss1XtUHJCIkAlk0Vi0S' }
{ plain: '$2b$10$S2g9jIcmjGxE0aT1ASd6lujHqT87kijqXTss1XtUHJCIkAlk0Vi0S',
  hash: '$2b$10$KRkVY3M8a8KX9FcZRX.l8.oTSupI/Fg0xij9lezgOxN8Lld7RCHXm' }

修复,解决方案

要解决此问题,您必须修改pre(&#34; save&#34;)代码,以确保密码仅在第一次保存到数据库或已被修改时进行哈希处理。为此,请在这些块中包围预存代码:

if (user.isModified("password") || user.isNew) {
    //Perform password hashing here
} else {
    return next();
}

以下是整个预保存功能的样子

UsersSchema.pre("save", function (next: NextFunction): any {
    let user: any = this;
    if (user.isModified("password") || user.isNew) {
        bcrypt.genSalt(10, function (err: Error, salt: string): any {
            if (err) {
                return next(err);
            }
            bcrypt.hash(user.password, salt, function (err: Error, hash: string) {
                if (err) {
                    console.log(err);
                    return next(err);
                }
                console.warn({plain: user.password, hash: hash});
                user.password = hash;
                next();
            });
        });
    } else {
        return next();
    }
});

希望这有助于某人。

答案 2 :(得分:0)

我将其放在此处,因为有一天有可能对某人有所帮助。

在我自己的情况下,即使我提供了正确的身份验证详细信息,我也仍然拥有bcrypt.compare as false的原因是由于模型中数据类型的约束。因此,每次将散列保存在数据库中时,它都会被截断以适应50字符约束。

我有

    'password': {
      type: DataTypes.STRING(50),
      allowNull: false,
      comment: "null"
    },

该字符串只能包含50 characters,但bcrypt.hash的结果远不止于此。

FIX

我这样修改了模型DataTypes.STRING(255)

答案 3 :(得分:0)

提示:如果要切换

then().then()

块始终检查返回值。

答案 4 :(得分:0)

您可以随时检查数据库中密码字段的最大长度。确保它很大。就我而言,我已将其设置为 500。然后代码完美运行!