Использование BCrypt с Sequelize Model
Я пытаюсь использовать пакет bcrypt-nodejs
с моей моделью sequelize и пытаюсь следовать учебному курсу, чтобы включить хеширование в мою модель, но я получаю сообщение об ошибке в generateHash
. Кажется, я не могу понять эту проблему. Есть ли лучший способ включить bcrypt?
Ошибка:
/Users/user/Desktop/Projects/node/app/app/models/user.js:26
User.methods.generateHash = function(password) {
^
TypeError: Cannot set property 'generateHash' of undefined
at module.exports (/Users/user/Desktop/Projects/node/app/app/models/user.js:26:27)
at Sequelize.import (/Users/user/Desktop/Projects/node/app/node_modules/sequelize/lib/sequelize.js:641:30)
Модель:
var bcrypt = require("bcrypt-nodejs");
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('users', {
annotation_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.DATE,
field: 'first_name'
},
lastName: {
type: DataTypes.DATE,
field: 'last_name'
},
email: DataTypes.STRING,
password: DataTypes.STRING,
}, {
freezeTableName: true
});
User.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
User.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
return User;
}
Ответы
Ответ 1
Методы должны быть представлены в аргументе "options" метода sequelize.define
const bcrypt = require("bcrypt");
module.exports = function(sequelize, DataTypes) {
const User = sequelize.define('users', {
annotation_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.DATE,
field: 'first_name'
},
lastName: {
type: DataTypes.DATE,
field: 'last_name'
},
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
freezeTableName: true,
instanceMethods: {
generateHash(password) {
return bcrypt.hash(password, bcrypt.genSaltSync(8));
},
validPassword(password) {
return bcrypt.compare(password, this.password);
}
}
});
return User;
}
Ответ 2
Другая альтернатива: Использовать режим асинхронного вызова hook и bcrypt
User.beforeCreate((user, options) => {
return bcrypt.hash(user.password, 10)
.then(hash => {
user.password = hash;
})
.catch(err => {
throw new Error();
});
});
Ответ 3
Там есть учебник о том, как получить систему ceelize/postgreSQL auth с помощью hooks и bcrypt.
Парень, который написал учебник, по какой-то причине не использовал асинхронные хеш-солевые методы (и я вижу, что код размещен повсюду без реальных ответов).
В любом случае, часы, когда я понимаю, что не знаю много о обещаниях или асинхронном javascript, я думаю, что я понял, как с этим справиться.
Асинхронные методы на самом деле настолько элегантны и красивы, я чувствую себя идиотом, но в любом случае:
В разделе создания/экземпляра пользователя он использовал следующий код:
hooks: {
beforeCreate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
}
}
Более новые версии Sequelize не похожи на методы экземпляра, объявляемые таким образом, и несколько человек объяснили, как исправить это (в том числе и тот, кто опубликовал исходный учебник):
В первоначальном комментарии все еще использовались синхронные методы, которые были немного разочаровывающими.
User.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
Очевидно, я идиот, потому что все, что вам нужно сделать, чтобы эти функции были асинхронны, такова:
Async beforeCreate bcrypt genSalt и genHash:
beforeCreate: async function(user) {
const salt = await bcrypt.genSalt(10); //whatever number you want
user.password = await bcrypt.hash(user.password, salt);
}
User.prototype.validPassword = async function(password) {
return await bcrypt.compare(password, this.password);
}
Для кода beforeCreate это буквально ничего не меняет - похоже, что это работает.
В приложении node.js на логинном пути, где вы проверяете пароль, есть раздел findOne:
User.findOne({ where: { username: username } }).then(function (user) {
if (!user) {
res.redirect('/login');
} else if (!user.validPassword(password)) {
res.redirect('/login');
} else {
req.session.user = user.dataValues;
res.redirect('/dashboard');
}
});
Все, что вам нужно сделать, это добавить слова async
и await
:
User.findOne({ where: { username: username } }).then(async function (user) {
if (!user) {
res.redirect('/login');
} else if (!await user.validPassword(password)) {
res.redirect('/login');
} else {
req.session.user = user.dataValues;
res.redirect('/dashboard');
}
});
Если я сделал что-то не так, пожалуйста, дайте мне знать - спасибо большое, надеюсь, это поможет кому-то там.