Подтвердить что ты не робот

Передать значение из внутренней в внешнюю функцию - Не удается сохранить пароль

Я пытаюсь использовать хэш-коды с 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)), когда внутренний имеет предпределенный, фиксированный синтаксис (crypto.pbkdf2), поэтому я не знаю, смогу ли я его отредактировать.

Как я могу оставить код как есть и все еще передать justCrypted обратно на psw (см. изменения в коде)? Если бы это была функция, которую я написал, я мог бы использовать apply, я думаю, но, crypto.pbkdf2 предваряется, и я не знаю, могу ли добавить к нему материал.

Спасибо

4b9b3361

Ответ 1

Проблема связана с областью, в настоящее время запрос измененный user.pswrd выходит за пределы области запроса, поэтому он возвращается к значению, присвоенному вверху.

Перемещая запрос внутри блока 'crypto.pbkdf2'..., значение user.pswrd будет работать по назначению. Я обновил ваш код (и сделал асинхронный солевой генерации, так как вы все равно использовали асинхронную версию 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");
})

Ответ 2

Чтобы ответить на ваше редактирование, я думаю, вам просто нужно понять, что crypto.pbkdf2 - это асинхронная функция, которая имеет функцию обратного вызова в качестве последнего параметра.

Итак, внутри этой функции обратного вызова вы можете получить доступ к переменной psw, и вы можете сделать что-то вроде psw = newlyCreatedId. Однако в вашем коде запрос, скорее всего, называется до. Таким образом, вы не можете использовать переменную psw в запросе, поскольку ваш код стоит.

То, что сделал Эшли Б, состоит в том, чтобы поместить вашу функцию query в обратный вызов, чтобы гарантировать, что запрос не вызывается до тех пор, пока криптофункция не будет выполнена. Вы также можете структурировать код для использования событий или promises, если вы не хотите вставлять функции.

Ответ 3

Это ответ на ваш EDIT и помочь вам понять передачу данных между внутренними и внешними функциями:

Я немного изменил код, чтобы продемонстрировать суть. То, что вы должны понимать, - это область и контекст вызывающей функции и способ активации вашей функции. Функция Crypto.pbkdf2 принимает функцию в качестве обратного вызова. Эта функция не является частью регулярного/последовательного потока управления. Функция pbkdf2 является "асинхронной" функцией - она ​​возвращается немедленно, но обработка может продолжаться на фоне. Вот почему код, помеченный '//Output 1: This will be undefined' ниже, выведет 'undefined'. Основной поток продолжается, пока pbkdf2 продолжает обработку в фоновом режиме.

Когда обработка системы завершена, она вызывает функцию, указанную вами в качестве обратного вызова, чтобы сообщить о ее завершенной обработке. Подумайте об этом как о сообщении, которое вы отправляете кому-то, или к телефону. Они отвечают на ответ с помощью SMS, когда они получают шанс.

Однако, когда система вызывает функцию обратного вызова, "область" или "контекст" различны (подумайте об этом как о внешнем объекте, вызывающем функцию обратного вызова). Поэтому он не знает, какие области/переменные/функции в вашем контексте. Чтобы передавать данные между функцией обратного вызова и вашими объектами, вы можете использовать метод '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

Посмотрите здесь, чтобы понять, как работает привязка, и здесь, чтобы понять концепцию этого и контекста в javascript.

EDIT. В ответ на ваш последний комментарий.

Если вы просто хотите передать justCrypted из pbkdf2 в var psw из User.prototype.save, вы можете использовать синхронную версию того же метода:

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, чтобы закончить возврат значения до того, как он переместится на следующую строку, таким образом, после последовательного потока.

Надеюсь, что это поможет.

EDIT 2: Ниже приведена небольшая диаграмма работы async:

введите описание изображения здесь

К сожалению, вы просто не можете сделать это так, как хотите, не используя внешнюю функцию или синхронную функцию.