Как защитить поле пароля в Mongoose/MongoDB, чтобы он не возвращался в запрос при заполнении коллекций?
Предположим, что у меня есть две коллекции/схемы. Одна из них - это схема пользователей с полями имени пользователя и пароля, затем у меня есть схема блогов, в которой есть ссылка на схему пользователей в поле автора. Если я использую Mongoose, чтобы сделать что-то вроде
Blogs.findOne({...}).populate("user").exec()
У меня будет документ в блоге и пользователь, который заполняется, но как я могу запретить Mongoose/MongoDB возвращать поле пароля? Поле пароля хэшируется, но оно не должно быть возвращено.
Я знаю, что могу опустить поле пароля и возвращать остальные поля в простой запрос, но как это сделать с заполнением. Кроме того, есть ли элегантный способ сделать это?
Кроме того, в некоторых ситуациях мне нужно получить поле пароля, например, когда пользователь хочет войти или изменить пароль.
Ответы
Ответ 1
.populate('user' , '-password')
http://mongoosejs.com/docs/populate.html
Ответ JohnnyHKs с использованием опций схемы, вероятно, является способом перехода сюда.
Также обратите внимание, что query.exclude()
существует только в ветке 2.x.
Ответ 2
Вы можете изменить поведение по умолчанию на уровне определения схемы с помощью атрибута select
поля:
password: { type: String, select: false }
Затем вы можете потянуть его по мере необходимости в сообщениях find
и populate
с помощью выбора поля в качестве '+password'
. Например:
Users.findOne({_id: id}).select('+password').exec(...);
Ответ 3
Изменить:
Попробовав оба подхода, я обнаружил, что исключающий всегда подход не работает для меня по какой-то причине с использованием паспортно-локальной стратегии, не знаю почему.
Итак, вот что я в итоге использовал:
Blogs.findOne({_id: id})
.populate("user", "-password -someOtherField -AnotherField")
.populate("comments.items.user")
.exec(function(error, result) {
if(error) handleError(error);
callback(error, result);
});
Нет ничего плохого в том, что исключение всегда подходит, оно почему-то не срабатывало с паспортом, мои тесты сказали мне, что на самом деле пароль был исключен/включен, когда я захотел. Единственная проблема с включением всегда заключается в том, что мне в основном нужно пройти каждый вызов, который я делаю в базе данных, и исключить пароль, который является большой работой.
После нескольких отличных ответов я обнаружил, что есть два способа сделать это: "всегда включать и исключать иногда" и "всегда исключать и включать иногда"?
Пример обоих:
Включить всегда, но исключать иногда пример:
Users.find().select("-password")
или
Users.find().exclude("password")
exlucde всегда, но иногда включает пример:
Users.find().select("+password")
но вы должны определить в схеме:
password: { type: String, select: false }
Ответ 4
User.find().select('-password')
- правильный ответ. Вы не можете добавить select: false
в схему, поскольку она не будет работать, если вы хотите войти в систему.
Ответ 5
Вы можете добиться этого, используя схему, например:
const UserSchema = new Schema({/* */})
UserSchema.set('toJSON', {
transform: function(doc, ret, opt) {
delete ret['password']
return ret
}
})
const User = mongoose.model('User', UserSchema)
User.findOne() // This should return an object excluding the password field
Ответ 6
Предполагая, что ваше поле пароля является "паролем", вы можете просто сделать:
.exclude('password')
Ниже представлен более обширный пример здесь
Это сосредоточено на комментариях, но это тот же самый принцип в игре.
Это то же самое, что использовать проекцию в запросе в MongoDB и передавать {"password" : 0}
в поле проекции. См. здесь
Ответ 7
Решение состоит в том, чтобы никогда не хранить незашифрованные пароли. Вам следует использовать такой пакет, как bcrypt или хэш пароля.
Пример использования для хеширования пароля:
var passwordHash = require('password-hash');
var hashedPassword = passwordHash.generate('password123');
console.log(hashedPassword); // sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97
Пример использования для подтверждения пароля:
var passwordHash = require('./lib/password-hash');
var hashedPassword = 'sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97';
console.log(passwordHash.verify('password123', hashedPassword)); // true
console.log(passwordHash.verify('Password0', hashedPassword)); // false
Ответ 8
Я использую поле скрытого пароля в ответе REST JSON
UserSchema.methods.toJSON = function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
module.exports = mongoose.model('User', UserSchema);
Ответ 9
Вы можете передать объект DocumentToObjectOptions в schema.toJSON() или schema.toObject().
См. определение TypeScript из @types/mongoose
/**
* The return value of this method is used in calls to JSON.stringify(doc).
* This method accepts the same options as Document#toObject. To apply the
* options to every document of your schema by default, set your schemas
* toJSON option to the same argument.
*/
toJSON(options?: DocumentToObjectOptions): any;
/**
* Converts this document into a plain javascript object, ready for storage in MongoDB.
* Buffers are converted to instances of mongodb.Binary for proper storage.
*/
toObject(options?: DocumentToObjectOptions): any;
DocumentToObjectOptions имеет параметр преобразования, который запускает пользовательскую функцию после преобразования документа в объект javascript. Здесь вы можете скрыть или изменить свойства в соответствии с вашими потребностями.
Итак, допустим, вы используете schema.toObject() и хотите скрыть путь к паролю из вашей пользовательской схемы. Вы должны настроить общую функцию преобразования, которая будет выполняться после каждого вызова toObject().
UserSchema.set('toObject', {
transform: (doc, ret, opt) => {
delete ret.password;
return ret;
}
});
Ответ 10
Blogs.findOne({ _id: id }, { "password": 0 }).populate("user").exec()
Ответ 11
При использовании password: { type: String, select: false }
вы должны иметь в виду, что он также будет исключать пароль, когда он нам понадобится для аутентификации. Поэтому будьте готовы обращаться с ним, как хотите.
Ответ 12
Это скорее следствие первоначального вопроса, но это был вопрос, с которым я столкнулся, пытаясь решить мою проблему...
А именно, как отправить пользователя обратно клиенту в обратном вызове user.save() без поля пароля.
Случай использования: пользователь приложения обновляет информацию/настройки своего профиля от клиента (пароль, контактную информацию, whatevs). Вы хотите отправить обновленную информацию о пользователе обратно клиенту в ответ, как только он успешно будет сохранен в mongoDB.
User.findById(userId, function (err, user) {
// err handling
user.propToUpdate = updateValue;
user.save(function(err) {
// err handling
/**
* convert the user document to a JavaScript object with the
* mongoose Document toObject() method,
* then create a new object without the password property...
* easiest way is lodash _.omit function if you're using lodash
*/
var sanitizedUser = _.omit(user.toObject(), 'password');
return res.status(201).send(sanitizedUser);
});
});
Ответ 13
Я нашел другой способ сделать это, добавив некоторые настройки в конфигурацию схемы.
const userSchema = new Schema({
name: {type: String, required: false, minlength: 5},
email: {type: String, required: true, minlength: 5},
phone: String,
password: String,
password_reset: String,
}, { toJSON: {
virtuals: true,
transform: function (doc, ret) {
delete ret._id;
delete ret.password;
delete ret.password_reset;
return ret;
}
}, timestamps: true });
Добавив функцию преобразования в объект toJSON с именем поля, которое нужно исключить. как указано в документах:
Нам может потребоваться выполнить преобразование результирующего объекта на основе по некоторым критериям, скажем, удалить некоторую конфиденциальную информацию или вернуть пользовательский объект. В этом случае мы устанавливаем дополнительную функцию transform
.