使用 Mongoose 模式验证密码/确认密码

Posted

技术标签:

【中文标题】使用 Mongoose 模式验证密码/确认密码【英文标题】:Validating password / confirm password with Mongoose schema 【发布时间】:2012-12-08 13:30:19 【问题描述】:

我有一个userSchema,看起来像这样:

var userSchema = new Schema(
    name: 
      type: String
    , required: true
    , validate: [validators.notEmpty, 'Name is empty']
    
  , username: 
      type: String
    , required: true
    , validate: [validators.notEmpty, 'Username is empty']
    
  , email: 
      type: String
    , required: true
    , validate: [
         validator: validators.notEmpty, msg: 'Email is empty' 
      ,  validator: validators.isEmail, msg: 'Invalid email' 
      ]
    
  , salt: String
  , hash: String
);

到目前为止,我的所有验证都在模式中进行,我想知道如何通过密码验证来实现这一点。用户在两个字段中输入密码,模型应该检查它们是否相同。

这种验证是否属于架构?我是这种验证的新手。

我应该如何验证密码?

【问题讨论】:

【参考方案1】:

我最终发现,您可以结合使用 虚拟路径invalidate 函数来实现此目的,如本要点所示,用于匹配密码的相同目的:https://gist.github.com/1350041

直接引用:

CustomerSchema.virtual('password')
.get(function() 
  return this._password;
)
.set(function(value) 
  this._password = value;
  var salt = bcrypt.gen_salt_sync(12);
  this.passwordHash = bcrypt.encrypt_sync(value, salt);
);

CustomerSchema.virtual('passwordConfirmation')
.get(function() 
  return this._passwordConfirmation;
)
.set(function(value) 
  this._passwordConfirmation = value;
);

CustomerSchema.path('passwordHash').validate(function(v) 
  if (this._password || this._passwordConfirmation) 
    if (!val.check(this._password).min(6)) 
      this.invalidate('password', 'must be at least 6 characters.');
    
    if (this._password !== this._passwordConfirmation) 
      this.invalidate('passwordConfirmation', 'must match confirmation.');
    
  

  if (this.isNew && !this._password) 
    this.invalidate('password', 'required');
  
, null);

【讨论】:

如果需要,您还可以在 virtuals set() 函数调用中添加 this.invalidate() 调用。 我尝试实现该解决方案,但 this._password 在更新时未定义。是否应该针对较新的软件版本进行更正? (node v. 0.10.25, express v. 1.3.10, mongoose v. 1.3.10) 完美!我有同样的问题,解决方案是“_”【参考方案2】:

我认为密码匹配属于客户端界面,永远不应该到达服务器(DB层已经太多了)。对于用户体验来说,最好不要仅仅为了告诉用户 2 个字符串不同而进行服务器往返。

至于瘦控制器,胖模型......所有这些灵丹妙药都应该射回鼻祖。在任何情况下都没有好的解决方案。考虑他们每个人的背景。

在这里引入胖模型的想法,使您可以将功能(模式验证)用于完全不同的目的(密码匹配),并使您的应用依赖于您现在使用的技术。有一天你会想要改变技术,你会得到一些根本没有模式验证的东西......然后你必须记住你的应用程序的那部分功能依赖于它。而且您必须将其移回客户端或控制器。

【讨论】:

很好的解释。谢谢你。我目前依靠模型来验证大部分数据。但是,就像您说的那样,对于密码检查之类的事情,这是不切实际的。我很想知道如何继续使用上面的模型验证以及控制器中的密码匹配?显然,密码验证将首先出现,如果通过了,只有这样才会进行其他验证。您是否建议将我的所有验证移至控制器或使用控制器和模型中的验证方法? 在 node.js 上,验证器模块就是您要寻找的。在这里找到它:github.com/chriso/node-validator。至于推荐:这实际上取决于您在做什么:如果您在多个应用程序与多个开发人员之间共享这些模型定义,而不是在模型中进行更多验证会有所帮助。如果只有您和一个应用程序,将验证放在控制器中,虽然会使控制器更加冗长,但消除了部分魔法,并且当您遇到某些类型的错误时可能会导致调试减少。 谢谢,我想这回答了我的疑问。在此处发布在单独的线程中:***.com/questions/13993669/… 我正在使用模型中已有的验证器模块:validators.notEmpty 我认为,如果您想就是否应该这样做发表意见,这很好,但您应该将其作为对问题的评论发布,而不是作为对问题的回答。跨度> 【参考方案3】:

我知道线程很旧,但如果它可以节省某人的时间...... 我的方法使用预验证钩子,非常适合我

schema.virtual('passwordConfirmation')
    .get(function() 
      return this._passwordConfirmation;
    )
    .set(function(value) 
        this._passwordConfirmation = value;
    );

schema.pre('validate', function(next) 
    if (this.password !== this.passwordConfirmation) 
        this.invalidate('passwordConfirmation', 'enter the same password');
    
    next();
);

【讨论】:

什么是无效的? 也可以在 next() 参数中返回错误,而不是使用 invalidate【参考方案4】:

我在 express-validator 降到 ./routes/signup.js 中的架构级别之前使用它:

exports.post = function(req, res)
  req.assert('email', 'Enter email').notEmpty().isEmail();
  req.assert('username', 'Enter username').notEmpty().isAlphanumeric().len(3,20);
  req.assert('password', 'Enter password').notEmpty().notContains(' ').len(5,20);

  res.locals.err = req.validationErrors(true);

  if ( res.locals.err ) 
    res.render('signup',  message:  error: 'Woops, looks like we need more info...' );
    return;
  

  ...//save
;

【讨论】:

我正在这样做,但我知道最好在模型中这样做。 “瘦控制器,胖模型。” 如果您使用回调,您可以很容易地向 UI 显示错误。【参考方案5】:

您可以通过向Schema.methods 添加新的函数属性来将自定义方法附加到模型实例(您也可以使用Schema.statics 创建模式函数。)下面是一个验证用户密码的示例:

userSchema.methods.checkPassword = function(password) 
    return (hash(password) === this.password);
;

// You could then check if a user's password is valid like so:
UserModel.findOne( email: 'email@gmail.com' , function(err, user) 
    if (user.checkPassword('secretPassword')) 
        // ... user is legit
    
);

【讨论】:

这是正确的做法吗?其余的验证在模型中完成,错误来自save 方法(参见代码)。我不确定我是否会将模型中的错误与这样的错误结合起来。 newUser.save(function (err) if (err) return res.redirect('/register'); res.send('registered'); );【参考方案6】:

注册时无需提交二次验证密码。 您可能会在客户端验证这两个字段是否相等而侥幸。

【讨论】:

【参考方案7】:

有点晚了,但为了人们有类似的问题。我最近遇到了类似的问题,这就是我的解决方法;我使用了一个名为 joi 的库

const joi = require('joi');
     ...
function validateUser(user)
  const schema = joi.object(
    username: joi.string().min(3).max(50).required(),
    email: joi.string().min(10).max(255).required().email(),
    password: joi.string().min(5).max(255).required(),
    password2: joi.string().valid(joi.ref('password')).required(),
  );

  return schema.validate(user);


exports.validate = validateUser;

【讨论】:

以上是关于使用 Mongoose 模式验证密码/确认密码的主要内容,如果未能解决你的问题,请参考以下文章

验证密码和确认密码

使用引导验证器验证密码并确认密码字段

[密码并以角度8的反应形式确认密码验证

在 PHP 中验证密码并确认密码

使用 parsley js 进行表单验证。字母数字和密码确认

密码和确认密码字段验证 angular2 反应形式