Supongamos que tengo dos colecciones / esquemas. Uno es el esquema de usuarios con campos de nombre de usuario y contraseña, luego, tengo un esquema de blogs que tiene una referencia al esquema de usuarios en el campo del autor. Si uso Mongoose para hacer algo como
Blogs.findOne({...}).populate("user").exec()
También tendré el documento del Blog y el usuario, pero ¿cómo puedo evitar que Mongoose / MongoDB devuelva el campo de contraseña? El campo de contraseña tiene un hash, pero no debe devolverse.
Sé que puedo omitir el campo de contraseña y devolver el resto de los campos en una consulta simple, pero ¿cómo lo hago con rellenar? Además, ¿hay alguna forma elegante de hacer esto?
Además, en algunas situaciones, necesito obtener el campo de contraseña, como cuando el usuario quiere iniciar sesión o cambiar la contraseña.
Respuestas:
.populate('user' , '-password')
http://mongoosejs.com/docs/populate.html
La respuesta de JohnnyHK usando las opciones de esquema es probablemente el camino a seguir aquí.
También tenga en cuenta que
query.exclude()
solo existe en la rama 2.x.fuente
Puede cambiar el comportamiento predeterminado en el nivel de definición de esquema utilizando el
select
atributo del campo:password: { type: String, select: false }
Luego, puede ingresarlo según sea necesario
find
ypopulate
llamar mediante la selección de campo como'+password'
. Por ejemplo:Users.findOne({_id: id}).select('+password').exec(...);
fuente
Editar:
Después de probar ambos enfoques, descubrí que el enfoque de excluir siempre no funcionaba para mí por alguna razón al usar la estrategia local de pasaporte, realmente no sé por qué.
Entonces, esto es lo que terminé usando:
Blogs.findOne({_id: id}) .populate("user", "-password -someOtherField -AnotherField") .populate("comments.items.user") .exec(function(error, result) { if(error) handleError(error); callback(error, result); });
No hay nada de malo en el enfoque de excluir siempre, simplemente no funcionó con el pasaporte por alguna razón, mis pruebas me dijeron que, de hecho, la contraseña estaba siendo excluida / incluida cuando quería. El único problema con el enfoque de incluir siempre es que básicamente necesito pasar por cada llamada que hago a la base de datos y excluir la contraseña, lo cual es mucho trabajo.
Después de un par de excelentes respuestas, descubrí que hay dos formas de hacer esto, "incluir siempre y excluir a veces" y "excluir siempre e incluir a veces".
Un ejemplo de ambos:
El ejemplo incluir siempre pero excluir a veces :
Users.find().select("-password")
o
Users.find().exclude("password")
El exlucde siempre pero incluye a veces el ejemplo:
Users.find().select("+password")
pero debes definir en el esquema:
password: { type: String, select: false }
fuente
.select("+field")
, solo trae el__id
, aunque.select("-field")
excluye muy bien el campo que quieroselect: false
es obligatorioUser.find().select('-password')
es la respuesta correcta. No puede agregarselect: false
el esquema, ya que no funcionará si desea iniciar sesión.fuente
const query = model.findOne({ username }).select("+password");
y usarlo en las rutas de inicio de sesión y cambio / restablecimiento de contraseña y, de lo contrario, asegurarse de que nunca salga. Esto es mucho más seguro para que no regrese de forma predeterminada, ya que se ha demostrado que las personas cometen errores en mi opiniónPuede lograrlo usando el esquema, por ejemplo:
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
fuente
Estoy usando para ocultar el campo de contraseña en mi respuesta REST JSON
UserSchema.methods.toJSON = function() { var obj = this.toObject(); //or var obj = this; delete obj.password; return obj; } module.exports = mongoose.model('User', UserSchema);
fuente
Suponiendo que su campo de contraseña es "contraseña", puede hacer lo siguiente:
.exclude('password')
Hay un ejemplo más extenso aquí.
Eso se centra en los comentarios, pero es el mismo principio en juego.
Esto es lo mismo que usar una proyección en la consulta en MongoDB y pasar
{"password" : 0}
el campo de proyección. Ver aquifuente
Blogs.findOne({ _id: id }, { "password": 0 }).populate("user").exec()
fuente
La solución es nunca almacenar contraseñas de texto sin formato. Debería utilizar un paquete como bcrypt o password-hash .
Ejemplo de uso para hash la contraseña:
var passwordHash = require('password-hash'); var hashedPassword = passwordHash.generate('password123'); console.log(hashedPassword); // sha1$3I7HRwy7$cbfdac6008f9cab4083784cbd1874f76618d2a97
Ejemplo de uso para verificar la contraseña:
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
fuente
Puede pasar un objeto DocumentToObjectOptions a schema.toJSON () o schema.toObject () .
Consulte la definición de TypeScript de @ 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 tiene una opción de transformación que ejecuta una función personalizada después de convertir el documento en un objeto javascript. Aquí puede ocultar o modificar propiedades para satisfacer sus necesidades.
Entonces, digamos que está usando schema.toObject () y desea ocultar la ruta de la contraseña de su esquema de usuario. Debe configurar una función de transformación general que se ejecutará después de cada llamada a toObject ().
UserSchema.set('toObject', { transform: (doc, ret, opt) => { delete ret.password; return ret; } });
fuente
Encontré otra forma de hacer esto, agregando algunas configuraciones a la configuración del esquema.
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 });
Añadiendo la función de transformación al objeto toJSON con el nombre de campo para excluir. como se indica en los documentos :
fuente
router.get('/users',auth,(req,res)=>{ User.findById(req.user.id) //skip password .select('-password') .then(user => { res.json(user) }) })
fuente
Mientras lo usa
password: { type: String, select: false }
, debe tener en cuenta que también excluirá la contraseña cuando la necesitemos para la autenticación. Así que prepárate para manejarlo como quieras.fuente
Esto es más un corolario de la pregunta original, pero esta fue la pregunta que encontré al intentar resolver mi problema ...
Es decir, cómo enviar al usuario de vuelta al cliente en la devolución de llamada user.save () sin el campo de contraseña.
Caso de uso: el usuario de la aplicación actualiza la información / configuración de su perfil desde el cliente (contraseña, información de contacto, lo que sea). Desea enviar la información de usuario actualizada al cliente en la respuesta, una vez que se haya guardado correctamente en 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's toObject() method, * then create a new object without the password property... * easiest way is lodash's _.omit function if you're using lodash */ var sanitizedUser = _.omit(user.toObject(), 'password'); return res.status(201).send(sanitizedUser); }); });
fuente
const userSchema = new mongoose.Schema( { email: { type: String, required: true, }, password: { type: String, required: true, }, }, { toJSON: { transform(doc, ret) { delete ret.password; delete ret.__v; }, }, } );
fuente