Mongoose如何安全取消临时选中字段 避免敏感字段泄露到接口响应
Mongoose 敏感字段安全隐藏方案
Mongoose 本身没有内置的.unselect()实例方法,但可以通过以下两种无副作用的方案实现需求,既不会破坏后续.save()持久化逻辑,也能从根源避免敏感字段随接口响应返回。
方案一:全局Schema配置(推荐,一劳永逸)
这是最适配你场景的方案,全应用自动生效,不需要修改任何现有业务逻辑,也不需要在校验完成后手动处理字段。
在定义Schema时,给toJSON和toObject添加转换钩子,仅在文档序列化输出(也就是res.json()触发的转换流程)时自动剔除敏感字段,完全不影响文档内存中的字段值,也不会干扰后续保存操作:
const userSchema = new mongoose.Schema({ // 原有字段定义,包含email、phoneNum、name等 name: String, password_createTime: { type: Date, select: false } }, { // 序列化JSON时自动处理 toJSON: { transform(doc, ret) { delete ret.password_createTime; return ret; } }, // 调用toObject转普通对象时同步处理 toObject: { transform(doc, ret) { delete ret.password_createTime; return ret; } } })
方案特性
- 校验阶段可以正常读取临时查询的
password_createTime做token有效性判断 - 后续路由修改
name等字段调用.save()时,完全不会触碰password_createTime字段,不会出现undefined覆盖库中数据的问题 - 所有接口返回用户文档时,会自动剔除敏感字段,不需要每个路由单独处理
方案二:自定义unselect实例方法(贴合你期望的调用方式)
如果你希望在校验完成后手动控制字段移除时机,可以直接在Schema上挂载自定义实例方法,实现你预期的.unselect()调用效果:
// 在userSchema上挂载自定义方法 userSchema.methods.unselect = function(path) { // 标记对应路径为忽略状态,后续save操作不会追踪、持久化该字段的任何变更 this.$ignore(path); // 从文档底层存储对象中移除该字段,保证序列化输出时不会携带 delete this._doc[path]; return this; }
配置完成后就可以完全按照你期望的写法使用:
const userDoc = await User.findById(THE_USER_ID).select('+password_createTime') if (userDoc.password_createTime > someDate) return next(new Error('the password got changed, your token is expired')) userDoc.unselect('password_createTime') // 完全符合预期的调用方式 req.loggedInUser = userDoc
避坑提醒
以下常见操作都属于会影响数据的不良实践,不要使用:
- 直接给
userDoc.password_createTime赋值undefined:Mongoose会追踪到该字段的变更,save时会将undefined写入数据库 - 调用
userDoc.$unset('password_createTime'):该方法会生成数据库删除操作,永久删除库中对应字段 - 直接
delete userDoc.password_createTime:该操作仅删除Mongoose包装的访问器属性,底层_doc中存储的字段值依然存在,序列化时还是会返回给前端
内容的提问来源于stack exchange,提问作者Islam Y-
相关产品推荐
相关产品推荐

