当System.console()返回null时如何处理Java密码读取?

5

我正在编写一个命令行程序,需要提示用户输入密码,但不想在屏幕上显示出密码字符。经过一些搜索,我发现System.console().readPassword()这个方法很适合我的需求,但是在Unix管道处理时会出现问题。因此,当我像下面这样调用示例程序时:

% java PasswdPrompt

当我这样调用时,控制台为null,导致失败:

% java PasswdPrompt | less

或者

% java PasswdPrompt < inputfile

依我看,这似乎是一个JVM问题,但我不可能是唯一遇到这个问题的人,所以我想肯定有些解决方案。

有人知道吗?

提前感谢。

import java.io.Console;

public class PasswdPrompt {
    public static void main(String args[]) {
        Console cons = System.console();
        if (cons == null) {
            System.err.println("Got null from System.console()!; exiting...");
            System.exit(1);
        }
        char passwd[] = cons.readPassword("Password: ");
        if (passwd == null) {
            System.err.println("Got null from Console.readPassword()!; exiting...");
            System.exit(1);
        }
        System.err.println("Successfully got passwd.");
    }
}

刚在Ubuntu 10.10下测试了一下,发现出现了同样的错误,所以你不是唯一一个遇到这个问题的人。 - Yanick Rochon
2个回答

0

来自Java 文档页面:

如果System.console返回NULL,则不允许控制台操作,这可能是因为操作系统不支持它们或程序在非交互环境中启动。

问题很可能是因为使用管道导致退出“交互”模式,而使用输入文件将其用作System.in,因此没有Console

** 更新 **

这里有一个快速修复方法。在您的main方法末尾添加以下行:

if (args.length > 0) {
   PrintStream out = null;
   try {
      out = new PrintStream(new FileOutputStream(args[0]));
      out.print(passwd);
      out.flush();
   } catch (Exception e) {
      e.printStackTrace();
   } finally {
      if (out != null) out.close();
   }
}

然后像这样调用您的应用程序

$ java PasswdPrompt .out.tmp; less .out.tmp; rm .out.tmp

然而,您输入的密码将以明文形式存储在隐藏文件中,直到命令终止。


谢谢你的回答。问题在于这很愚蠢——底层 shell 仍然分配了 tty,并且仍然可以进行所有相关的系统调用,所以我无法理解 JVM 做了什么来破坏它。 - capveg
JVM 实际上并没有出现任何问题。问题在于它没有使用 "/dev/tty" 来获取控制台。(或者也许它是的...但设备文件的名称在您的平台上不同。) - Stephen C
1
我很感谢你的帮助,但是你提供的解决方法并不是我能够合理要求我的用户每次运行我的工具时都这样做的。也许我错了,但我无法想象我是唯一遇到这个问题的人,所以一定有某种合理的解决方案。无论如何,你的解决方案没有处理第二个用例“java PasswdPrompt < input”。是的,我可以添加另一个类似的临时解决方法,但最终,我只是希望这个命令行工具能像其他命令行Unix工具一样正常运行。再次感谢你。 - capveg
只需创建一个Shell脚本来运行您的程序。 - Yanick Rochon

0

因此,由于某种原因,当System.console()返回null时,终端回显总是关闭的,因此我的问题变得微不足道。以下代码完全按照我想要的方式工作。感谢所有的帮助。

import java.io.*;


public class PasswdPrompt {
    public static void main(String args[]) throws IOException{
        Console cons = System.console();
        char passwd[];
        if (cons == null) {
            // default to stderr; does NOT echo characters... not sure why
            System.err.print("Password: ");
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                                            System.in));
            passwd= reader.readLine().toCharArray();
        }
        else {
            passwd = cons.readPassword("Password: ");
        }
        System.err.println("Successfully got passwd.: " + String.valueOf(passwd));
    }

}

2
我不确定你的解决方案是否理想。基本上,这意味着密码必须在你的应用程序正在读取的文件或管道中。许多人认为这是不安全的。 - Stephen C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接