从内部函数传递值到外部函数 - 无法保存密码

时间:2015-12-24 22:52:47

标签: javascript asynchronous scope callback

我尝试使用crypto哈希密码,但我无法将其保存在数据库中。

我有node.js 4.2.3 express 4.13.3,我的数据库是PostgreSQL 9.1。该字段为character varying (255),名为pswrd

这是我的代码:

var tobi = new User({
    usrnm:'sp',
    pswrd:'an'
});

module.exports = User;

function User(obj){
    for(var key in obj){
        this[key] = obj[key];
    }
}

User.prototype.save = function (fn){
    var user=this;
     //EDIT, added this :
     var psw ;
    var salt = crypto.randomBytes(50).toString('base64');
    crypto.pbkdf2(user.pswrd, salt, 10000, 150, 'sha512',function(err, derivedKey) {
        //user.pswrd = derivedKey.toString('hex');
         //EDIT, added this:
         var justCrypted = derivedKey.toString('hex');
    });

    var query=client.query('INSERT INTO mytable(usrnm,pswrd)VALUES($1,$2) RETURNING mytable_id',[user.usrnm,user.pswrd], function(err, result) {
        if(err) {console.log(err)}
        else {
          var newlyCreatedId = result.rows[0].mytable_id;
        }
    });
    query.on("end", function (result) {console.log(result);client.end();});
}

tobi.save(function (err){
    if (err)throw error;
    console.log("yo");
})  

要运行此功能,请输入node lib/user。我没有错误,但密码未正确保存。保存第一个值an,而不是哈希值。我在这里缺少什么?

修改

AshleyB答案很好,但是,请帮助我了解如何将内部函数(crypto.pbkdf2)中的数据传递到其外部(User.prototype.save = function (fn)),当内部具有预定义的固定语法时({ {1}}),所以我不知道是否或如何编辑它。

如何保留代码并仍然将crypto.pbkdf2传递回justCrypted(请参阅代码编辑)?如果它是我写的一个函数,我可以使用psw,但是,apply是预先设定的,我不知道是否可以添加内容。

由于

3 个答案:

答案 0 :(得分:9)

问题在于范围,当前查询更改的user.pswrd超出了查询范围,因此它回退到顶部指定的值。

通过在'crypto.pbkdf2' ...块中移动查询,user.pswrd值将按预期工作。我已经更新了你的代码(并使salt生成异步,因为你已经使用了异步版本的pbkdf2)。

var tobi = new User({
  usrnm: 'sp',
  pswrd: 'an'
});

module.exports = User;

function User(obj) {
  for (var key in obj) {
    this[key] = obj[key];
  }
}

User.prototype.save = function(fn) {
  var user = this;


  // Changed salt generation to async version
  // See: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback

  crypto.randomBytes(50, function(ex, buf) {
  if (ex) throw ex;
  salt = buf.toString('base64');

    // password, salt, iterations, keylen[, digest], callback
    crypto.pbkdf2(user.pswrd, salt, 10000, 150, 'sha512', function(err, derivedKey) {
    user.pswrd = derivedKey.toString('hex');

        // Moved the query within the hash function
    var query = client.query('INSERT INTO mytable(usrnm,pswrd)VALUES($1,$2) RETURNING mytable_id', [user.usrnm, user.pswrd], function(err, result) {
      if (err) {
        console.log(err)
      } else {
        var newlyCreatedId = result.rows[0].mytable_id;
      }
    });
    query.on("end", function(result) {
      console.log(result);
      client.end();
    });

  });


    });

}


tobi.save(function(err) {
  if (err) throw error;
  console.log("yo");
})

答案 1 :(得分:1)

要回答您的编辑,我认为您只需要了解crypto.pbkdf2是一个异步函数,它具有回调函数作为它的最后一个参数。

因此,在该回调函数中,您可以访问psw变量,并且您可以执行psw = newlyCreatedId之类的操作。但是,在您的代码中,查询很可能在回调之前被称为 。因此,您无法在代码中使用查询中的psw变量。

Ashley B所做的是将query函数放在回调中,以确保在加密函数之后才调用查询。您还可以构建代码以使用事件,或者如果您不希望嵌套函数,则可以使用承诺。

答案 2 :(得分:1)

这是为了回答您的编辑并帮助您了解内部和外部函数之间的数据传递:

我稍微修改了你的代码以证明这一点。您必须了解的是调用函数的范围和上下文以及函数的调用方式。 Crypto.pbkdf2函数接受函数作为回调。此功能不是常规/顺序控制流程的一部分。 pbkdf2函数是一个“异步”函数 - 它会立即返回,但处理可以在后台继续。这就是下面标有 '//Output 1: This will be undefined' 的代码输出'undefined'的原因。当pbkdf2继续在后台处理时,主线程继续。

当系统完成处理时,它会调用您指定为回调的函数,以通知您已完成处理。您可以将其视为发送给某人的短信,而不是拨打电话。只要有机会,他们就会回复短信。

但是,当系统调用回调函数时,“范围”或“上下文”是不同的(将其视为调用回调函数的外部实体)。因此,它不知道您的上下文中的范围/变量/函数。为了在回调函数和对象之间传递数据,您可以使用'bind'方法和'this'关键字:

var tobi = new User({
    usrnm:'sp',
    pswrd:'an'
});
module.exports = User;
function User(obj){
    for(var key in obj){
        this[key] = obj[key];
    }
}
User.prototype.save = function (fn){
    var user=this;

     var psw ; //Change this to this.psw to pass changes back.
     var crypto = require('crypto');
    var salt = crypto.randomBytes(50).toString('base64');
    crypto.pbkdf2(user.pswrd, salt, 10000, 150, 'sha512',function(err, derivedKey) {

         var justCrypted = derivedKey.toString('hex');
         console.log('Finished crypting' + justCrypted);
         this.psw = justCrypted;
//When we use 'this' here, we set the value in the right scope/context.
//The var psw does not exist outside the scope of this function, so the
//external function will not know about it. By defining it in the current
// (this) scope, we ensure that external function is also aware of it.
         this.externalFunction(justCrypted);
//Bind the callback function to the current context - it will remember 
//this when the callback gets invoked. Think of it as explicitly specifying
//the 'context' of the call/method.
    }.bind(this));
    //Output 1: This will be undefined as callback has not returned yet.
    console.log('New psw' + this.psw); 

    /*var query=client.query('INSERT INTO mytable(usrnm,pswrd)VALUES($1,$2) RETURNING mytable_id',[user.usrnm,user.pswrd], function(err, result) {
        if(err) {console.log(err)}
        else {
          var newlyCreatedId = result.rows[0].mytable_id;
        }
    });
    query.on("end", function (result) {console.log(result);client.end();}); */
}
User.prototype.externalFunction = function (someData) {
    console.log('Data passed from an internal function');
    console.log('New PSW: ' + this.psw);
    console.log('Data from external function: ' + someData);
}
tobi.save(function (err){
    if (err)throw error;
    console.log("yo");
})

如果我运行上面的命令,我会看到以下输出。请注意,数据现在通过回调在内部和外部函数之间传递:

C:\temp\node\npm-test>node index.js
New pswundefined
3f9219ae14f9246622973724ace5cb66b190a4b5e86abf482fce5d7889e6aff870741d672ff78e7765fada25f150c70e5e61
c13b5bcdec634d03830668348b5a7d06cf75f426259dcf804241eb2f4362d10f1ebf23a1aecca28072a3f38fb8a39eba88c9
f055e9e7ccabafcd8caed25d8b26f3726022973175545f77e4024bcbcc657081ea5d1f2baf5e9080cbb20696135f2be8834c

Data passed from an internal function
New PSW: 3f9219ae14f9246622973724ace5cb66b190a4b5e86abf482fce5d7889e6aff870741d672ff78e7765fada25f15
0c70e5e61c13b5bcdec634d03830668348b5a7d06cf75f426259dcf804241eb2f4362d10f1ebf23a1aecca28072a3f38fb8a
39eba88c9f055e9e7ccabafcd8caed25d8b26f3726022973175545f77e4024bcbcc657081ea5d1f2baf5e9080cbb20696135
f2be8834c
Data from external function: 3f9219ae14f9246622973724ace5cb66b190a4b5e86abf482fce5d7889e6aff870741d6
72ff78e7765fada25f150c70e5e61c13b5bcdec634d03830668348b5a7d06cf75f426259dcf804241eb2f4362d10f1ebf23a
1aecca28072a3f38fb8a39eba88c9f055e9e7ccabafcd8caed25d8b26f3726022973175545f77e4024bcbcc657081ea5d1f2
baf5e9080cbb20696135f2be8834c

查看here以了解绑定的工作原理,here了解javascript中此操作和上下文的概念。

编辑:回复您的最新评论。

如果您只想将pCkdf2中的justCrypted传递给User.prototype.save的var psw,则可以使用相同方法的synchronous version

crypto.pbkdf2Sync(password, salt, iterations, keylen[, digest])

您的方法将如下所示:

User.prototype.save = function (fn){
    var user=this;
    var psw ;
    var crypto = require('crypto');
    var salt = crypto.randomBytes(50).toString('base64');
    psw = crypto.pbkdf2Sync(user.pswrd, salt, 10000, 150, 'sha512');
    var justCrypted = psw.toString('hex');
    console.log(justCrypted);
    ....
    ....
}

这里的一个重要区别是,这是一个同步方法,代码将等待crypto.pbkdf2Sync在移动到下一行之前完成返回值,从而遵循顺序流程。

希望有所帮助。

编辑2:以下是异步工作原理的一个小图表:

enter image description here

不幸的是,如果不使用外部函数或同步函数,就无法按照自己的方式进行操作。