如何保护Mongoose / MongoDB中的密码字段,以便在填充集合时不会在查询中返回?

时间:2012-08-23 16:31:43

标签: node.js mongodb express mongoose

假设我有两个集合/模式。一个是带有用户名和密码字段的用户架构,然后,我有一个Blogs架构,它在作者字段中引用了用户架构。如果我使用Mongoose做类似

的事情
Blogs.findOne({...}).populate("user").exec()

我将有Blog文档和用户填充,但是如何阻止Mongoose / MongoDB返回密码字段?密码字段经过哈希处理,但不应返回。

我知道我可以省略密码字段并在简单查询中返回其余字段,但是如何使用populate执行此操作。此外,有没有优雅的方法来做到这一点?

此外,在某些情况下,我需要获取密码字段,例如当用户想要登录或更改密码时。

15 个答案:

答案 0 :(得分:226)

您可以使用字段的select属性更改架构定义级别的默认行为:

password: { type: String, select: false }

然后,您可以根据需要在findpopulate来电中通过字段选择'+password'将其拉入。例如:

Users.findOne({_id: id}).select('+password').exec(...);

答案 1 :(得分:52)

.populate('user' , '-password')

http://mongoosejs.com/docs/populate.html

使用Schema选项的JohnnyHKs回答可能就是这里的方法。

另请注意,query.exclude()仅存在于2.x分支中。

答案 2 :(得分:15)

编辑:

在尝试这两种方法之后,我发现使用护照本地策略的排除总是方法对我来说不起作用,不知道为什么。

所以,这就是我最终使用的内容:

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 }

答案 3 :(得分:9)

User.find().select('-password')是正确的答案。如果要登录,则无法在架构上添加select: false,因为它不起作用。

答案 4 :(得分:6)

您可以使用架构实现这一目标,例如:

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

答案 5 :(得分:3)

假设您的密码字段是“密码”,您可以这样做:

.exclude('password')

有一个更广泛的示例here

这主要集中在评论上,但它的原理相同。

这与在MongoDB中使用查询中的投影并在投影字段中传递{"password" : 0}相同。见here

答案 6 :(得分:2)

解决方案是永远不存储纯文本密码。您应该使用bcryptpassword-hash之类的软件包。

使用示例哈希密码:

 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

答案 7 :(得分:1)

Blogs.findOne({ _id: id }, { "password": 0 }).populate("user").exec()

答案 8 :(得分:1)

使用password: { type: String, select: false }时,请记住,当我们需要密码进行身份验证时,它还将排除密码。因此,请准备好随心所欲地处理它。

答案 9 :(得分:1)

我在我的REST JSON响应中用于隐藏密码字段

UserSchema.methods.toJSON = function() {
 var obj = this.toObject();
 delete obj.password;
 return obj;
}

module.exports = mongoose.model('User', UserSchema);

答案 10 :(得分:0)

这是原始问题的必然结果,但这是我遇到的问题,试图解决我的问题......

即,如何在没有密码字段的情况下将用户发送回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'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);
    });
});

答案 11 :(得分:0)

您可以将 DocumentToObjectOptions 对象传递给 schema.toJSON() schema.toObject()

请参阅@types/mongoose中的TypeScript定义

 /**
 * 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;
  }
});

答案 12 :(得分:0)

通过向架构配置添加一些设置,我找到了另一种方法。

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 });

通过将转换函数添加到具有排除字段名称的JSON对象。如在docs stated中:

  

我们可能需要对结果对象进行转换   根据某些条件,例如说删除一些敏感信息或返回   自定义对象。在这种情况下,我们设置了可选的transform函数。

答案 13 :(得分:0)

router.get('/users',auth,(req,res)=>{
   User.findById(req.user.id)
    //skip password
    .select('-password')
    .then(user => {
        res.json(user)
    })
})

答案 14 :(得分:0)

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;
      },
    },
  }
);