重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
目前 对于 Java 命令行基于文本的输入/输出 API 的批评之一就是它缺乏对命令行输入口令屏蔽的支持 如果借助 AWT/Swing 这便不再成为问题 因为 AWT/Swing 提供了可以提供屏蔽口令的方法
创新互联公司专业为企业提供永和网站建设、永和做网站、永和网站设计、永和网站制作等企业网站建设、网页设计与制作、永和企业网站模板建站服务,十载永和做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
年 月 我发表了本文的早期版本 其后便不断收到大量感谢信 建设性的意见和在应用程序中使用源代码的许可 本文
概述了口令屏蔽描述了用于口令屏蔽的 AWT/Swing 实用程序为命令行输入口令屏蔽问题提供独立于平台的解决方案为口令屏蔽提供一个改进的解决方案(可靠而安全)
口令屏蔽
登录屏幕和登录对话框使用口令屏蔽技术 这种技术要么在输入口令时隐藏口令 要么显示一个字符(比如星号 * )来代替用户输入的字符 例如 当您在一台 Windows 机器上进行登录时 一个登录对话框将会呈现在您眼前 其中的口令一栏使用星号作为屏蔽或回显字符
如果操作系统是 UNIX 则登录屏幕中的口令栏不显示回显字符 它的做法很简单 就是什么都不显示
AWT/Swing 中的口令屏蔽
如果您希望为您的应用程序提供图形化的登录对话框 您可以使用 AWT 的 TextField 类 该类是一个文本组件 允许编辑单行文本 为了屏蔽口令栏 要使用 setEchoChar 方法 例如 为了把回显字符设置为星号 您需要这样做
TextField password = new TextField( );password setEchoChar( * );
基于所使用字体的平均字符宽度 数字 指定了文本栏的宽度 您可以把回显字符设置为任何您喜欢的字符 注意 如果您把它设置为 这意味着输入将会被回显 而不会被屏蔽
在 Swing 中 您可以使用 JPasswordField 它允许编辑单行文本 视图表明正在输入内容 但是不会显示原始字符 JPasswordField 类与和 setEchoChar 一起使用的 AWT 的 TextField 是源代码兼容的 如果您使用 JPasswordField 默认的回显字符是星号 * 但是您可以将其修改为任何您选定的字符 此外 如果您把回显字符设置为 这意味着字符将在输入时显示出来 而不会被屏蔽 图 显示了一个 Swing 登录对话框 其中的回显字符被设置为 # 使用的是下面的代码片断
JPasswordField password = new JPasswordField( );password setEchoChar( # );
命令行输入屏蔽
和 AWT/Swing 不同 在 Java 中没有特殊的 API 可用于屏蔽命令行输入 这也是许多开发人员一直所要求的一项功能 如果您希望为命令行基于文本的 Java 应用程序以及服务器端 Java 应用程序提供一个登录屏幕 它就很有用 提供这种功能的一种方式就是使用 Java 本地接口(Java Native Interface JNI) 对于不了解 C/C++ 或者希望坚持 % 纯 Java 代码的某些 Java 开发人员来说 这可能有一定难度
这里我针对这个问题提出一个解决方案 在本文的早期版本中 所使用的是一个 UNIX 风格的登录屏幕 口令根本不在屏幕上回显 这样做的具体方法是 让一个单独的线程通过重写和打印口令提示命令行 尝试擦除回显到控制台的字符 大家仍然可以从论坛下载该篇文章中专用的代码和改进后的代码
然而 大家最需要的功能之一是使用星号 * 替换回显的字符 因此 本文从为口令屏蔽提供一个简单的解决方案开始 接着给出改进后的 更加可靠和安全的代码
简单的解决方案
这个解决方案使用一个单独的线程 在输入回显字符的时候擦除它们 然后使用星号代替它们 这是使用 EraserThread 类来完成的 如代码示例 所示
代码示例 EraserThread java
import java io *;
class EraserThread implements Runnable { private boolean stop;
/** *@param The prompt displayed to the user */ public EraserThread(String prompt) { System out print(prompt); }
/** * Begin masking display asterisks (*) */ public void run () { stop = true; while (stop) { System out print( \ * ); try { Thread currentThread() sleep( ); } catch(InterruptedException ie) { ie printStackTrace(); } } }
/** * Instruct the thread to stop masking */ public void stopMasking() { this stop = false; }}
注意 这个解决方案广泛利用了线程 然而 如果机器负载很重 就不能确保 MaskingThread 能够足够经常地运行 请继续阅读本文的余下部分来了解代码的改进版本
PasswordField 类使用了 EraserThread 类 这一点在代码示例 中体现出来了 这个类提示用户输入口令 而且 EraserThread 的一个实例尝试使用 * 屏蔽输入 注意 一开始将显示一个星号 (*)
代码示例 PasswordField java
public class PasswordField {
/** *@param prompt The prompt to display to the user *@return The password as entered by the user */ public static String readPassword (String prompt) { EraserThread et = new EraserThread(prompt); Thread mask = new Thread(et); mask start();
BufferedReader in = new BufferedReader(new InputStreamReader(System in)); String password = ;
try { password = in readLine(); } catch (IOException ioe) { ioe printStackTrace(); } // stop masking et stopMasking(); // return the password entered by the user return password; }}
作为如何使用 PasswordField 类的一个例子 考虑应用程序 TestApp 如示例代码 所示 这个应用程序显示一条提示 并等待用户输入口令 当然 输入被屏蔽为星号(*)
代码示例 TestApp java
class TestApp { public static void main(String argv[]) { String password = PasswordField readPassword( Enter password: ); System out println( The password entered is: +password); }}
如果您在 Windows MacOS 或 UNIX 操作系统上运行 TesApp 您将会发现其输出与图 类似 此外还要注意 当您运行该应用程序时 会显示一个初始的星号
使代码安全而可靠
上述的简单解决方案有一个主要缺陷 不应该使用字符串来存储诸如口令这类敏感信息!在本文的余下部分中 将会给出一个经过改进的解决方案
然而 首先 MaskingThread 类能够从几处改进中获益
为了确保跨线程的可见性 尤其是在多 CPU 的机器上 stop 字段应该被标记为 volatile volatile 关键字指定同步线程使用该字段 这样编译器就不会对它进行任何优化 换句话说 应该从内存读取变量的值 而不应该在堆栈中保存任何拷贝
为了确保屏蔽能够在系统高负荷运转时也能够出现 在调用持续期间 调用线程的优先权被设定为最大 返回时再恢复其原始的优先权
代码示例 显示了修订后的 MaskingThread 类 修改的地方均以粗体形式突出显示
import java io *;
/*** This class attempts to erase characters echoed to the console */
class MaskingThread extends Thread { private volatile boolean stop; private char echochar = * ;
/** *@param prompt The prompt displayed to the user */ public MaskingThread(String prompt) { System out print(prompt); }
/** * Begin masking until asked to stop */ public void run() {
int priority = Thread currentThread() getPriority(); Thread currentThread() setPriority(Thread MAX_PRIORITY);
try { stop = true; while(stop) { System out print( \ + echochar); try { // attempt masking at this rate Thread currentThread() sleep( ); }catch (InterruptedException iex) { Thread currentThread() interrupt(); return; } } } finally { // restore the original priority Thread currentThread() setPriority(priority); } }
/** * Instruct the thread to stop masking */ public void stopMasking() { this stop = false; }}
尽管使用 Strings 收集和存储口令看起来似乎很合逻辑 它们并不适合存储诸如口令这样的敏感信息 这是因为 Strings 类型的对象是不可改变的——使用后不能重写或修改字符串的内容 应该使用一个 chars 数组作为代替 修订后的 PasswordField 如代码示例 所示 它是根据 Using Password Based Encryption 改写而来
代码示例 PasswordField java
import java io *;import java util *;
/*** This class prompts the user for a password and attempts to mask input with * */
public class PasswordField {
/** *@param input stream to be used (e g System in) *@param prompt The prompt to display to the user *@return The password as entered by the user */
public static final char[] getPassword(InputStream in String prompt) throws IOException {
MaskingThread maskingthread = new MaskingThread(prompt); Thread thread = new Thread(maskingthread); thread start();
char[] lineBuffer; char[] buf; int i;
buf = lineBuffer = new char[ ];
int room = buf length; int offset = ; int c;
loop: while (true) { switch (c = in read()) { case : case : break loop;
case \r : int c = in read(); if ((c != ) (c != )) { if (!(in instanceof PushbackInputStream)) { in = new PushbackInputStream(in); } ((PushbackInputStream)in) unread(c ); } else { break loop; }
default: if ( room ) { buf = new char[offset + ]; room = buf length offset ; System arraycopy(lineBuffer buf offset); Arrays fill(lineBuffer ); lineBuffer = buf; } buf[offset++] = (char) c; break; } } maskingthread stopMasking(); if (offset == ) { return null; } char[] ret = new char[offset]; System arraycopy(buf ret offset); Arrays fill(buf ); return ret; }}
最后 PasswordApp 类如代码示例 所示 它只是一个用于测试修订后代码的测试应用程序
代码示例 PasswordApp java
import java io *;
lishixinzhi/Article/program/Java/hx/201311/25934
原因是因为 开始的两句代码 就是要输入用户名和密码的代码
String name = inputUserName();
String password = inputUserPassword();
就是说你的代码是先要输入用户名和密码 才会依次进行判断的。
所以像你所说的那种效果的代码应该是:
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
if (checkNmae(inputUserName())) {
if (checkPassword(inputUserPassword())) {
System.out.println("Check OK");
} else {
System.out.println("Wrong password");
}
} else {
System.out.println("Wrong name");
}
}
public static String inputUserName() {
Scanner userNmae = new Scanner(System.in);
System.out.println("Please Input Your Name:");
String name = userNmae.nextLine();
return name;
}
public static String inputUserPassword() {
Scanner userPassword = new Scanner(System.in);
System.out.println("Please Input Your Password:");
String password = userPassword.nextLine();
return password;
}
public static boolean checkNmae(String name) {
return ("ZYH".equals(name));
}
public static boolean checkPassword(String password) {
return ("1305080102".equals(password));
}
}
给个思路:
1.定义一个数组,将数字,大小写字母,特殊字符都包含在内。
2用random类随即产生下标数字。然后打印数组对应的下标。