1 前言
大家知道,在登录Microsoft Windows操作系统过程中,在登录窗口中需要用户输入注册用户名称和密码。细心的读者会发现:用户输入的注册用户名称内容为原码字符显示,而注册密码内容为掩码字符显示。例如,同样在两各文本区中输入字符"a",在用户名文本区中显示的是字符"a",而在密码区中显示的字符为掩码"*",这就是所谓的密码屏蔽输入。将输入的密码屏蔽回显,不仅增强了用户私有信息的安全性,更重要的是维护了计算机系统的稳定性和安全性。
Java以其语言的面向对象能力、高安全性和Java平台的系统无关性等技术优势,在商务软件开发过程中赢得了众多程序设计人员的青睐。JDK在AWT和JFC类库中定义了用于密码字符屏蔽的应用程序设计接口(API),使应用系统开发人员在编写图形用户界面程序时,能够灵活地定义密码回显方式。但是,对于基于命令行方式的Java应用程序,JDK没有定义相应的密码屏蔽策略,程序设计人员必须编写字符回显控制代码。本文将通过对实例代码的分析,对基于JDK平台开发Java应用程序的密码屏蔽输入方法进行探讨,主要内容包括:
●AWT组件对象密码屏蔽方法
●JSwing组件对象密码屏蔽方法
●Java命令行程序密码屏蔽方法
2 利用AWT组件实现密码屏蔽输入
Java抽象窗口工具包(Abstract Window Toolkit,AWT)是在JDK1.0版本中定义的用于编写Java图形用户界面程序的应用程序设计接口,程序设计人员可以利用该包中定义的多种类型组件对象,编写具有用户界面的应用程序。
为了实现用户输入信息的屏蔽,可以利用AWT组件库中定义的TextField对象,该对象的定义继承结构如下:
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.TextComponent
|
+--java.awt.TextField
在该对象中,定义了用于设置和维护用户输入字符回显方式的方法,这些方法的定义形式为:
char getEchoChar():获取用户定义的文本区回显字符;
boolean echoCharIsSet()判断是否定义了回显字符;
void setEchoChar(char c):设置文本区回显字符为字符c。
因此,在读者编写的Java程序中,可以在创建TextField对象实例后,例如上述方法控制文本区回显字符的方式。下面的程序完整地演示了回显字符的定义方式:
//PasswordMaskingDemo.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.JOptionPane;
public class PasswordMaskingDemo
{
public static void main(String args[])
{
final Frame frmFrame = new Frame();
Panel pnlPanel = new Panel();
Label lblUsername = new Label("用户名");
Label lblPassword = new Label("密码");
final TextField txtUsername = new TextField("Anyomonus");
final TextField txtPassword = new TextField("", 8);
txtUsername.setEditable(false);
txtPassword.setEchoChar(´*´);
Button btnButton1 = new Button("登录");
Button btnButton2 = new Button("其它用户登录");
Button btnButton3 = new Button("关闭");
btnButton1.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if( (txtPassword.getText()).length() == 0 )
{
JOptionPane.showMessageDialog
(frmFrame, "密码不能为空");
return;
}
txtPassword.setColumns(16);
System.out.println("Anyomonus用户的密码:" +
txtPassword.getText() );
}
});
btnButton2.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
txtUsername.setEditable(true);
}
});
btnButton3.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
pnlPanel.add(lblUsername);
pnlPanel.add(txtUsername);
pnlPanel.add(lblPassword);
pnlPanel.add(txtPassword);
pnlPanel.add(btnButton1);
pnlPanel.add(btnButton2);
pnlPanel.add(btnButton3);
frmFrame.add(pnlPanel);
frmFrame.setTitle("演示TextField对象的应用方法");
frmFrame.pack();
frmFrame.show();
}
}
该程序运行的窗口形式如下图所示:
screen.width-333)this.width=screen.width-333;">
图 1 PasswordMaskingDemo.java 程序运行窗口
在上述程序中,与文本区回 显 控制相 关 的代 码为 :
final TextField txtPassword = new TextField("", 8);
txtPassword.setEchoChar(´*´);
在上面的代 码 中,首先 创 建初始内容 为 空、可 输 入8个字符的TextField 对 象 实 例txtPassword后,利用TextField 对 象中定 义 的setEchoChar方法 设 置 该 文本区的回 显 字符 为 星号"*",从而 实现输 入字符的掩 码 。 因此 总结为 :可以利用 TextField 对 象中定 义 的 setEchoChar 方法, 实现 AWT 组 件 对 象程序中的字符掩 码输 入和回 显 控制。
3 利用 JSwing 组 件 对 象 实现 密 码 屏蔽 输 入
JSwing 组 件 对 象是 轻 量 级 Java 组 件 对 象,其中定 义 了多 种组 件 对 象 类 型,而且其外 观 也更加新 颖 。与 AWT 组 件 对 象相 对应 ,在 JSwing 组 件 对 象中也定 义 了 JTextField 对 象,用于用 户进 行文本 输 入。那 么 , 读 者是否会 联 想利用 对 象中也定 义 的 setEchoChar 方法定 义 回 显 字符呢? 实际 情况不是 这样 。在 JTextField 对 象中没有定 义该 方法,而是以 JTextField 为 父 对 象,定 义 了用于 进 行密 码输 入的文本区 对 象 JPasswordField , 该对 象的定 义继 承 结 构如下:
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Container
|
+--javax.swing.JComponent
|
+--javax.swing.text.JTextComponent
|
+--javax.swing.JTextField
|
+--javax.swing.JPasswordField
在 JPasswordField 对 象中,定 义 了多 种类 型用于控制字符回 显 方式的方法,其中 setEchoChar 方法即用于定 义 文本区回 显 字符,如下面代 码 段所示:
… …
JPasswordField password = new JPasswordField(8);
password.setEchoChar(´*´);
… …
与 AWT 组 件 对 象定 义 文本区的回 显 方式 类 似,上述代 码创 建了 JPasswordField 对 象 实 例后, 设 置 该对 象的回 显 字符 为 星号 "*" 。但是, 读 者需要注意的是:在 JSwing 对 象中,需要利用 JPasswordField 对 象来管理用 户输 入文本。
4 Java 命令行程序密 码 屏蔽 输 入 实现 方式
与基于 AWT 或者 JSwing 的 图 形用 户 界面程序相比,在基于命令行的 Java 程序中 实现 密 码 屏蔽 输 入要比 较 麻 烦 一些,原因在于 JDK 没有提供任何基于文本的字符回 显 控制方法,因此需要 编 写相 应 的控制代 码 。基于通用性方面的考 虑 ,在本文中将 编 写用于屏蔽用 户输 入的 对 象 InputMasking , 该对 象的定 义 如下:
//InputMasking.java
import java.io.*;
public class InputMasking
{
String getPassword(String initial) throws IOException
{
MaskingThread listeningthread = new MaskingThread(initial);
Thread thread_instance = new Thread(listeningthread);
String password = "";
thread_instance.start();
while (true)
{
char input = (char)System.in.read();
listeningthread.stopMasking();
if (input == ´ ´)
{
input = (char)System.in.read();
if (input == ´ ´)
break;
else
continue;
}
else if(input == ´ ´)
break;
else
password += input;
}
return password;
}
}
该对 象在后台启 动线 程的控制下,从系 统输 入 设备 中 读 取字符并 对该 字符 进 行分析。如果遇到行 结 束 标 志, 则 返回 该线 程 获 取的字符串 对 象 password 。 读 者一定会 关 心后台 线 程 对 象 MaskingThread 的作用, 该线 程 对 象周期地刷新 终 端窗口,其目的在于屏蔽用 户输 入的字符,使 该 字符不能 够 在窗口中 显 示出来。 该线 程 对 象的定 义为 :
//MaskingThread.java
import java.io.*;
class MaskingThread extends Thread
{
private boolean stop = false;
private int index;
private String initial;
public MaskingThread(String initial)
{
this.initial = initial;
}
public void run()
{
while(!stop)
{
try
{
this.sleep(1);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
if (!stop)
{
System.out.print(" " + initial + " " + initial);
}
System.out.flush();
}
}
public void stopMasking()
{
this.stop = true;
}
}
在 InputMasking 对 象和 MaskingThread 对 象的配合下,使得基于命令行的 Java 应 用程序能 够实现 用 户输 入密 码 字符的屏蔽,其核心方法是利用后台 线 程 时时 刷新 终 端窗口,屏蔽用 户输 入字符。下面的 CmdLineUtility 对 象即利用前面定 义 的两个 对 象 进 行用 户输 入屏蔽, 请读 者 实际 运行上述程序,以了解命令行 Java 程序屏蔽 输 入的方式:
//CmdLineUtility.java
import java.io.*;
public class CmdLineUtility
{
public static void main(String argv[])
{
InputMasking masking = new InputMasking();
String password = null;
try
{
password = masking.getPassword(" 请输 入登 录 密 码 : ");
}
catch(IOException ex)
{
ex.printStackTrace();
}
System.out.println(" 您 输 入的密 码为 : " + password);
}
}
5 结 束 语
本文着重 讲 解了基于 Java 语 言 编 写 图 形用 户 界面程序和命令行程序中, 实现 用 户输 入字符屏蔽的方法。从文中内容可以看出: 对 于 图 形用 户 界面程序,无 论 利用 AWT 组 件 对 象, 还 是利用 JSwing 组 件 对 象,均可以利用相 应 的 组 件 对 象并 调 用 对 象 实 例中定 义 的方法, 实现 用 户输 入字符的屏蔽,从而 简 化了代 码编 写 难 度。
对 于基于命令行的 Java 程序,由于 JDK 中没有定 义 相 应 的 Java 对 象,因此,需要 编 写屏蔽用 户输 入的代 码 ,将用 户输 入不 显 示在 终 端窗口中,从而 实现 用 户输 入屏蔽。