使用 node.js 从输入中获取密码

Get password from input using node.js

提问人:Alfred 提问时间:1/17/2011 更新时间:4/16/2022 访问量:23352

问:

如何使用 node.js 从输入中获取密码?这意味着您不应该输出在控制台中输入的密码。

输入 节点.js

评论


答:

5赞 Alfred 1/17/2011 #1

为此,我找到了这篇出色的 Google Group 帖子

其中包含以下代码片段:

var stdin = process.openStdin()
  , stdio = process.binding("stdio")
stdio.setRawMode()

var password = ""
stdin.on("data", function (c) {
  c = c + ""
  switch (c) {
    case "\n": case "\r": case "\u0004":
      stdio.setRawMode(false)
      console.log("you entered: "+password)
      stdin.pause()
      break
    case "\u0003":
      process.exit()
      break
    default:
      password += c
      break
  }
})

评论

0赞 mikemaccana 4/28/2012
注意 process.binding(“stdio”) 在当前版本的 node 中不再有效
16赞 mikemaccana 4/28/2012 #2

2015 年 12 月 13 日更新:readline 已替换,node_stdio已从节点 0.5.10 中删除process.stdin

var BACKSPACE = String.fromCharCode(127);


// Probably should use readline
// https://nodejs.org/api/readline.html
function getPassword(prompt, callback) {
    if (prompt) {
      process.stdout.write(prompt);
    }

    var stdin = process.stdin;
    stdin.resume();
    stdin.setRawMode(true);
    stdin.resume();
    stdin.setEncoding('utf8');

    var password = '';
    stdin.on('data', function (ch) {
        ch = ch.toString('utf8');

        switch (ch) {
        case "\n":
        case "\r":
        case "\u0004":
            // They've finished typing their password
            process.stdout.write('\n');
            stdin.setRawMode(false);
            stdin.pause();
            callback(false, password);
            break;
        case "\u0003":
            // Ctrl-C
            callback(true);
            break;
        case BACKSPACE:
            password = password.slice(0, password.length - 1);
            process.stdout.clearLine();
            process.stdout.cursorTo(0);
            process.stdout.write(prompt);
            process.stdout.write(password.split('').map(function () {
              return '*';
            }).join(''));
            break;
        default:
            // More passsword characters
            process.stdout.write('*');
            password += ch;
            break;
        }
    });
}

getPassword('Password: ', (ok, password) => { console.log([ok, password]) } );

评论

0赞 user2226755 6/4/2014
无法验证值。如果您按退格键或其他非字母数字的触摸。
0赞 Scruffy 9/27/2015
require('tty').setRawMode()已弃用。
4赞 Trent Mick 11/24/2012 #3

这是我从上面调整的 nailer 版本,更新为获取回调和节点 0.8 用法:

/**
 * Get a password from stdin.
 *
 * Adapted from <http://stackoverflow.com/a/10357818/122384>.
 *
 * @param prompt {String} Optional prompt. Default 'Password: '.
 * @param callback {Function} `function (cancelled, password)` where
 *      `cancelled` is true if the user aborted (Ctrl+C).
 *
 * Limitations: Not sure if backspace is handled properly.
 */
function getPassword(prompt, callback) {
    if (callback === undefined) {
        callback = prompt;
        prompt = undefined;
    }
    if (prompt === undefined) {
        prompt = 'Password: ';
    }
    if (prompt) {
        process.stdout.write(prompt);
    }

    var stdin = process.stdin;
    stdin.resume();
    stdin.setRawMode(true);
    stdin.resume();
    stdin.setEncoding('utf8');

    var password = '';
    stdin.on('data', function (ch) {
        ch = ch + "";

        switch (ch) {
        case "\n":
        case "\r":
        case "\u0004":
            // They've finished typing their password
            process.stdout.write('\n');
            stdin.setRawMode(false);
            stdin.pause();
            callback(false, password);
            break;
        case "\u0003":
            // Ctrl-C
            callback(true);
            break;
        default:
            // More passsword characters
            process.stdout.write('*');
            password += ch;
            break;
        }
    });
}

评论

0赞 6/26/2015
您可以使用 的额外大小写语句来处理退格键。如果到目前为止的密码不为空,则可以用于将光标向后移动一列;之后,您可以写一个空格,然后再次向后移动。"u007F"process.stdout.write('\033[<1>D')
0赞 coolaj86 12/13/2015
我更新了它以使用退格键,并将其与原始接受的答案合并,并添加了一个指向我计划继续改进的存储库的链接。
63赞 isaacs 11/24/2012 #4

为此,您可以使用阅读模块(披露:由我编写):

在 shell 中:

npm install read

然后在你的 JS 中:

var read = require('read')
read({ prompt: 'Password: ', silent: true }, function(er, password) {
  console.log('Your password is: %s', password)
})

更新现代版本(使用):await

const password = await read({
  prompt: "Password: ",
  silent: true,
  replace: "*" //optional, will print out an asterisk for every typed character 
};
console.log("Your password: " + password);

评论

2赞 paulolc 2/19/2013
github.com/isaacs/read - 在谷歌上搜索这个模块时遇到了一些麻烦,所以,为了方便起见,我在这里删除了一个指向它的链接。
2赞 mpen 9/11/2013
有同步版本吗?
1赞 josh3736 10/24/2013
@Mark:怎么会有?stdin 是一个流,用于触发事件。如果函数正在等待返回,则无法处理事件。
7赞 mpen 10/24/2013
@josh3736:我不知道,也许以同样的方式工作??readFileSync
0赞 bryanmac 12/10/2014
@josh3736:不是同步,但我使用模块 async.forEachSeries 或 async.forever 通过异步函数提出连续的输入问题。
1赞 Ciro Santilli OurBigBook.com 4/14/2022
今天避免回调@mpen的最佳解决方法是使用 / 顺便说一句:stackoverflow.com/a/71868483/895245asyncawait
2赞 Ciro Santilli OurBigBook.com 4/14/2022 #5

如何在不回调的情况下使用 read

使用 /,我们可以使用以下标准模式摆脱烦人的回调:asyncawait

const readCb = require('read')

async function read(opts) {
  return new Promise((resolve, reject) => {
    readCb(opts, (err, line) => {
      if (err) {
        reject(err)
      } else {
        resolve(line)
      }
    })
  })
}

;(async () => {
  const password = await read({ prompt: 'Password: ', silent: true })
  console.log(password)
})()

令人讨厌的是,您必须将 async/await 传播到整个调用堆栈,但这通常是要走的路,因为它清楚地标记了什么是异步的,或者不是异步的。

在“read”:“1.0.7”,Node.js v14.17.0上进行了测试。

TODO 如何防止 EAGAIN 错误,如果您稍后尝试使用 fs.readFileSync(0) 再次使用 stdin?

https://stackoverflow.com/a/10357818/895245 都会导致将来尝试从 stdin 读取 with 与 EAGAIN 中断。不知道如何解决这个问题。繁殖:readfs.readFileSync(0)

const fs = require('fs')
const readCb = require('read')

async function read(opts) {
  return new Promise((resolve, reject) => {
    readCb(opts, (err, line) => {
      resolve([err, line])
    })
  })
}

;(async () => {
  const [err, password] = await read({ prompt: 'Password: ', silent: true })
  console.log(password)
  console.log(fs.readFileSync(0).toString())
})()

艺术

#!/usr/bin/env node

const fs = require('fs')

var BACKSPACE = String.fromCharCode(127);

// Probably should use readline
// https://nodejs.org/api/readline.html
function getPassword(prompt, callback) {
    if (prompt) {
      process.stdout.write(prompt);
    }

    var stdin = process.stdin;
    stdin.resume();
    stdin.setRawMode(true);
    stdin.resume();
    stdin.setEncoding('utf8');

    var password = '';
    stdin.on('data', function (ch) {
        ch = ch.toString('utf8');

        switch (ch) {
        case "\n":
        case "\r":
        case "\u0004":
            // They've finished typing their password
            process.stdout.write('\n');
            stdin.setRawMode(false);
            stdin.pause();
            callback(false, password);
            break;
        case "\u0003":
            // Ctrl-C
            callback(true);
            break;
        case BACKSPACE:
            password = password.slice(0, password.length - 1);
            process.stdout.clearLine();
            process.stdout.cursorTo(0);
            process.stdout.write(prompt);
            process.stdout.write(password.split('').map(function () {
              return '*';
            }).join(''));
            break;
        default:
            // More passsword characters
            process.stdout.write('*');
            password += ch;
            break;
        }
    });
}

async function read(opts) {
  return new Promise((resolve, reject) => {
    getPassword(opts, (err, line) => {
      resolve([err, line])
    })
  })
}

;(async () => {
  const [err, password] = await read('Password: ')
  console.log(password)
  console.log(fs.readFileSync(0).toString())
})()

错误:

fs.js:614
  handleErrorFromBinding(ctx);
  ^

Error: EAGAIN: resource temporarily unavailable, read
    at Object.readSync (fs.js:614:3)
    at tryReadSync (fs.js:383:20)
    at Object.readFileSync (fs.js:420:19)
    at /home/ciro/test/main.js:70:18
    at processTicksAndRejections (internal/process/task_queues.js:95:5) {
  errno: -11,
  syscall: 'read',
  code: 'EAGAIN'
}

咨询地点: https://github.com/npm/read/issues/39

评论

1赞 mpen 4/16/2022
如果出现错误,您可以返回元组。您的错误可能是由于忘记关闭某些管道。例如,您必须读取 Line(例如reject()close())