尝试在异步函数中使用 bcrypt 对密码进行哈希处理

Posted

技术标签:

【中文标题】尝试在异步函数中使用 bcrypt 对密码进行哈希处理【英文标题】:Trying to hash a password using bcrypt inside an async function 【发布时间】:2018-07-25 18:37:47 【问题描述】:

从this question开始。

我觉得我快到了,但我对异步的不完全理解使我无法解决这个问题。我基本上只是尝试使用 bcrypt 对密码进行哈希处理,并决定分离 hashPassword 函数,以便我可以在应用程序的其他部分使用它。

hashedPassword 不断返回 undefined ......

userSchema.pre('save', async function (next) 

  let user = this
  const password = user.password;

  const hashedPassword = await hashPassword(user);
  user.password = hashedPassword

  next()

)

async function hashPassword (user) 

  const password = user.password
  const saltRounds = 10;

  const hashedPassword = await bcrypt.hash(password, saltRounds, function(err, hash) 

    if (err) 
      return err;
    

    return hash

  );

  return hashedPassword


【问题讨论】:

【参考方案1】:

bcrypt 异步散列应该是这样的

bcrypt.hash(password, saltRounds, function(err, hash) 
  if (err) 
     throw err;
  
  // Do whatever you like with the hash
);

如果您对同步和异步感到困惑。你需要阅读更多关于它们的信息。 那里有很多好文章。

【讨论】:

我想我需要更好地理解 bcrypt.hash async 函数才能理解幕后发生的事情。 刚去了解bcrypt函数本身。正如 Akash 所说,确实,bcrypt.hash 没有返回承诺,所以我需要将它包装在一个中。 这可能会帮助其他人***.com/a/52087581/1274820【参考方案2】:

await 等待bcrypt.hash 因为bcrypt.hash 没有 回报一个承诺。使用以下方法,将bcrypt 包装在一个promise 中,以便使用await

async function hashPassword (user) 

  const password = user.password
  const saltRounds = 10;

  const hashedPassword = await new Promise((resolve, reject) => 
    bcrypt.hash(password, saltRounds, function(err, hash) 
      if (err) reject(err)
      resolve(hash)
    );
  )

  return hashedPassword

更新:-

该库已添加代码以返回将使用的承诺 async/await 的可能,这是不可用的 早些时候。新的使用方式如下。

const hashedPassword = await bcrypt.hash(password, saltRounds)

【讨论】:

可能值得明确一点,您基本上是将bcrypt 包装在一个承诺中以便使用await。不错的解决方案! 我试图避免使用 Promise,但在这种情况下显然需要这样做。感谢您的解决方案 - 它有效:) @Modermo 我强烈建议使用 Promise,因为它是异步代码的未来。所有花哨的新 async/await 东西都固定在 Promise 之上,因此绝对值得一试。此外,它使编写复杂的异步代码变得非常非常容易。 同意。以我有限的经验,异步的东西使用起来要好得多。不过在这种情况下,我真的不知道如何避免使用 Promise,因为我希望 hashPassword 成为它自己的函数。 当然,异步/等待需要一个承诺。没有其他办法,因为你不能在回调中做类似async return <value> 的事情(尽管这真的很整洁)。【参考方案3】:

默认情况下,bcrypt.hash(password,10) 将作为承诺返回。请查看here

示例:运行代码,

var bcrypt= require('bcrypt');

let password = "12345";


var hashPassword = async function()
    console.log(bcrypt.hash(password,10));
    var hashPwd = await bcrypt.hash(password,10);
    console.log(hashPwd);


hashPassword();

输出:

Promise  <pending> 
$2b$10$8Y5Oj329TeEh8weYpJA6EOE39AA/BXVFOEUn1YOFC.sf1chUi4H8i

当你在 async 函数中使用 await 时,它会一直等到它从 Promise 中得到解决。

【讨论】:

我们如何找回密码? @LV98 哈希是一种单向函数,您将无法从哈希密码中检索密码。此外,如果您多次运行代码,您将看到 hashPwd 会随着您每次生成新的盐而发生变化。所以如果你想比较密码,你必须使用bcrypt.compare,因为几乎不可能生成相同的哈希。 @nick,我认为 Squirrel.98 是在询问您/我们如何检索生成的哈希。我想你可以在 await 语句运行后返回 hashPwd ...【参考方案4】:

使用方法bcrypt.hashSync(),它是开箱即用的Synchronous。

const hashedPassword = bcrypt.hashSync(password,saltRounds);

【讨论】:

hashSync() 是 hash 的同步版本,所以你不需要等待它。因为异步返回 promise,需要等待。您可以使用同步的,例如 const hashed = bcrypt.hashSync(password, saltRounds) 哦,其实你是对的。我当时没有意识到这一点。我已经编辑了我的回复。谢谢 @PrivateOmega 文档说,如果您在服务器上使用 bcrypt,则首选异步:关于定时攻击的说明为什么建议使用异步模式而不是同步模式?如果你在一个简单的脚本上使用 bcrypt,使用同步模式就很好了。但是,如果您在服务器上使用 bcrypt,则建议使用异步模式。这是因为 bcrypt 完成的散列是 CPU 密集型的,因此同步版本将阻止事件循环并阻止您的应用程序为任何其他入站请求或事件提供服务。异步版本使用不阻塞主事件循环的线程池。 @mike 是的,当然,你写的完全正确,我知道这一切。但我想当时我只是在回答这个问题。【参考方案5】:

您需要在文档中查看here。

接受回调的异步方法,回调时返回 Promise 如果 Promise 支持可用,则不指定。

因此,如果您的函数调用接受回调,那么您不能在其上使用await,因为此函数签名不会返回Promise。为了使用await,您需要删除回调函数。您也可以将其包装在 Promiseawait 中,但这有点矫枉过正,因为库已经提供了这样做的机制。

代码重构:

try 
   // I removed the callbackFn argument
   const hashedPassword = await bcrypt.hash(password, saltRounds)
 catch (e) 
   console.log(e)

【讨论】:

【参考方案6】:
const hashedPassword = (password, salt) => 
    return new Promise((resolve, reject) => 
        bcrpyt.hash(password, salt, (err, hash) => 
            if (err) reject();
            resolve(hash);
        );
    );
;
hashedPassword('password', 10).then((passwordHash) => 
    console.log(passwordHash);
);

【讨论】:

【参考方案7】:

遇到同样的问题... 通过从函数分配布尔值来解决:

compareHash = (password, hashedPassword) => 
if (!password || !hashedPassword) 
  return Promise.resolve(false);

return bcrypt.compare(password, hashedPassword);
 ;

这里的 2 个参数不会是未定义的,这是问题的原因。 并调用函数:

let hashCompare = this.compareHash(model.password, entity.password);

【讨论】:

以上是关于尝试在异步函数中使用 bcrypt 对密码进行哈希处理的主要内容,如果未能解决你的问题,请参考以下文章

带有 BCrypt 哈希的 AES 256。如何从数据库中检查用户密码? [复制]

如何将密码文本与 bcrypt 哈希值进行比较?

Bcrypt 在 Lumen 5.4 中不起作用:调用未定义的函数 bcrypt()

如何使用散列 bcrypt 版本更新数据库中的每个密码?

在 Android 应用程序中使用 jBCrypt 对密码进行加盐会导致长时间挂起

使用Bcrypt对密码进行加密与解密验证