乐闻世界logo
搜索文章和话题

How to protect the password field in mongoose and it wont return in a query

5 个月前提问
3 个月前修改
浏览次数52

6个答案

1
2
3
4
5
6

Mongoose 是一个 MongoDB 对象建模工具,设计用于在异步环境中工作。在 Mongoose 中保护密码字段通常需要两个步骤:加密密码和确保在查询时不包括密码字段。

加密密码

保护密码字段的第一步是在保存到数据库之前对其进行加密。通常,这是通过使用 bcrypt 或类似的库来实现的。bcrypt 是一个安全的方式,因为它可以对密码进行散列,并且包含了一个盐(salt)来保护密码免受彩虹表攻击。

在 Mongoose 中,你可以使用 pre-save 钩子来在文档保存之前自动加密密码。以下是一个例子:

javascript
const bcrypt = require('bcrypt'); const mongoose = require('mongoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ username: String, password: String, // 其他字段... }); // Pre-save hook 用于加密密码 userSchema.pre('save', async function(next) { // 当密码字段被修改时才进行加密 if (this.isModified('password') || this.isNew) { try { const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(this.password, salt); this.password = hash; next(); } catch (error) { next(error); } } else { next(); } }); const User = mongoose.model('User', userSchema); module.exports = User;

确保查询时不包括密码字段

即使密码被加密,你通常也不想在查询的响应中包含它。在 Mongoose 中,可以使用查询投影来排除特定字段,或者通过在 schema 中设置 select: false 选项来默认排除某些字段。

在 schema 设置中排除密码字段的例子:

javascript
const userSchema = new Schema({ username: String, password: { type: String, select: false }, // 其他字段... }); // ...其余的schema和模型定义

当你使用这种方式时,即使你执行了一个普通的查询,像是 User.find(),密码字段也不会被返回。如果你在某个特定的查询中需要密码字段,你可以使用 .select('+password') 来明确请求它:

javascript
User.findOne({ username: 'exampleUser' }).select('+password').exec((err, user) => { // 现在 `user` 对象将包括密码字段 // ... });

通过这两个步骤,Mongoose 可以帮助你确保密码字段得到妥善保护并在默认情况下不被查询返回。

2024年6月29日 12:07 回复

You can change the default behavior at the schema definition level using the select attribute of the field:

shell
password: { type: String, select: false }

Then you can pull it in as needed in find and populate calls via field selection as '+password'. For example:

shell
Users.findOne({_id: id}).select('+password').exec(...);
2024年6月29日 12:07 回复

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

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

JohnnyHKs answer using Schema options is probably the way to go here.

Also note that query.exclude() only exists in the 2.x branch.

2024年6月29日 12:07 回复

Edit:

After trying both approaches, I found that the exclude always approach wasn't working for me for some reason using passport-local strategy, don't really know why.

So, this is what I ended up using:

shell
Blogs.findOne({_id: id}) .populate("user", "-password -someOtherField -AnotherField") .populate("comments.items.user") .exec(function(error, result) { if(error) handleError(error); callback(error, result); });

There's nothing wrong with the exclude always approach, it just didn't work with passport for some reason, my tests told me that in fact the password was being excluded / included when I wanted. The only problem with the include always approach is that I basically need to go through every call I do to the database and exclude the password which is a lot of work.


After a couple of great answers I found out there are two ways of doing this, the "always include and exclude sometimes" and the "always exclude and include sometimes"?

An example of both:

The include always but exclude sometimes example:

shell
Users.find().select("-password")

or

shell
Users.find().exclude("password")

The exclude always but include sometimes example:

shell
Users.find().select("+password")

but you must define in the schema:

shell
password: { type: String, select: false }
2024年6月29日 12:07 回复

You can achieve that using the schema, for example:

shell
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
2024年6月29日 12:07 回复

User.find().select('-password') is the right answer. You can not add select: false on the Schema since it will not work, if you want to login.

2024年6月29日 12:07 回复

你的答案