java程序设计中密码屏蔽(awt/swing/command line)

原创 2004年08月02日 22:06:00

   在网上关于JAVA下密码屏蔽输入的文章比较多了,但是都不是多全面和详细,今天在http://java.sun.com上看到了此文章,打算把它翻译出来。因为经验不够,可能翻译的还有出入。

   本篇文章(指原文)第一次发表于2002年9月。在JAVA下,利用AWT/SWING对用户输入密码进行屏蔽是很方便的,但是缺少I/O API,因此在命令行下来实现像UNIX的PASSWORD shadow就不容易了。本文就利用AWT/SWING/COMMAND LINE三种情况都一一作了较详细的介绍。

 

Password Masking in AWT
---------------------------------
如果提供图形界面登录对话框,可以利用AWT组件库中定义的TextField对象,该对象的定义继承结构如下:

java.lang.Object
    |
    +--java.awt.Component
       |
       +--java.awt.TextComponent
          |
          +--java.awt.TextField

在该对象中,定义了用于设置和维护用户输入字符回显方式的方法,这些方法的定义形式为:
char getEchoChar():获取用户定义的文本区回显字符;
boolean echoCharIsSet()判断是否定义了回显字符;
void setEchoChar(char c):设置文本区回显字符为字符。
具体代码为:
TextField password = new TextField(8);
password.setEchoChar('*');
其中8为文本输入的最大字符数,如果为0的话,则密码不会被屏蔽。


Password Masking in Swing
---------------------------------
利用JSWING组件,可以用JPasswordField对象,它的继承结构为:
java.lang.Object
    |
    +--java.awt.Component
       |
       +--java.awt.Container
          |
          +--javax.swing.JComponent
             |
             +--javax.swing.text.JTextComponent
                |
                +--javax.swing.JTextField
                   |
                   +--javax.swing.JPasswordField

具体代码为:
JPasswordField password = new JPasswordField(8);
password.setEchoChar('#');


Command-Line Input Masking
----------------------------------
不像Awt/Swing,在Java中没有专门用于屏蔽命令行下文本输入的API.如果想提供基于命令行文本输入的JAVA应用程序,一种方法是用Java Native Interface(JNI),但是对于一不太熟悉C/C++的开发者,或者坚持要用100%纯JAVA的开发者是难了一些。

这里提供针对此问题一个解决方案,较早的写关于此问题的文章,是像UNIX一样,用一个独立的线程来屏蔽(擦除)回显的字符。这篇文章,现在可以在http://forum.java.sun.com/thread.jsp?forum=9&thread=490728看到。


简单的解决方法
================
用一个独立的线程,在字符输入时擦除回显的字符,并用星号(*)来替换。代码如下:


//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("/010*");
  try {
     Thread.currentThread().sleep(1);
         } catch(InterruptedException ie) {
            ie.printStackTrace();
         }
      }
   }

   /**
    * Instruct the thread to stop masking
    */
   public void stopMasking() {
      this.stop = false;
   }
}

(注:本方法运行运行大量线程,如果电脑承担较大的负荷,系统就不会保证线程运行的稳定性。)

下边的PasswordField类利用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;
   }
}

 
TestApp.java是对此方法的一个应用.

//TestApp.java

class TestApp {
   public static void main(String argv[]) {
      String password = PasswordField.readPassword("Enter password: ");
      System.out.println("The password entered is: "+password);
   }
}

 

让代码更安全和可靠
=======================
1.声明private volatile boolean stop;volatile关键字用于指定和进程同步进行,换句话说就是变量的值直接从内存中讲习取,不在堆栈中留有拷贝。
2.为了保证在担有重负荷的机器上稳定运行,设置线程的优先权更高。

//MaskingThread.java

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("/010" + echochar);
           try {
              // attempt masking at this rate
              Thread.currentThread().sleep(1);
           }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;
   }
}

在上边的程序我们看到,利用String数据类型来存储Password这样的敏感信息是不合适的,String对象是不可改变的,并且在用过之后是不可重写的,因此下边会用Char型来代替.

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[128];

      int room = buf.length;
      int offset = 0;
      int c;

      loop:   while (true) {
         switch (c = in.read()) {
            case -1:
            case '/n':
               break loop;

            case '/r':
               int c2 = in.read();
               if ((c2 != '/n') && (c2 != -1)) {
                  if (!(in instanceof PushbackInputStream)) {
                     in = new PushbackInputStream(in);
                  }
                  ((PushbackInputStream)in).unread(c2);
                } else {
                  break loop;
                }

                default:
                   if (--room < 0) {
                      buf = new char[offset + 128];
                      room = buf.length - offset - 1;
                      System.arraycopy(lineBuffer, 0, buf, 0, offset);
                      Arrays.fill(lineBuffer, ' ');
                      lineBuffer = buf;
                   }
                   buf[offset++] = (char) c;
                   break;
         }
      }
      maskingthread.stopMasking();
      if (offset == 0) {
         return null;
      }
      char[] ret = new char[offset];
      System.arraycopy(buf, 0, ret, 0, offset);
      Arrays.fill(buf, ' ');
      return ret;
   }
}

最后给出上述修正后的代码的一个应用PasswordApp Class

// PasswordApp.java

import java.io.*;

public class PasswordApp {
   public static void main(String argv[]) {
      char password[] = null;
      try {
         password = PasswordField.getPassword(System.in, "Enter your password: ");
      } catch(IOException ioe) {
         ioe.printStackTrace();
      }
      if(password == null ) {
         System.out.println("No password entered");
      } else {
         System.out.println("The password entered is: "+String.valueOf(password));
      }
   }
}
===================================================
注:本文转自http://java.sun.com,原始文章:http://java.sun.com/developer/technicalArticles/Security/pwordmask/
原作者:Qusay H. Mahmoud

Java密码屏蔽输入的实现方法

原文地址:http://www.fengfly.com/plus/view-10100-1.html
  • u010555688
  • u010555688
  • 2014年04月09日 21:42
  • 1713

Java源码-简单计算器界面的AWT实现与Swing实现之比较

AWT(Abstract Windown Toolkit)调用操作系统自身的方法呈现界面,swing则是完全由Java代码实现。 AWT实现的界面效果如下: Swing实现的界面效果如...
  • hpdlzu80100
  • hpdlzu80100
  • 2016年07月28日 17:38
  • 2532

JAVA AWT图形用户界面设计

1 用AWT生成图形化用户界面   抽象窗口工具包AWT (Abstract Window Toolkit) 是 API为Java 程序提供的建立图形用户界面GUI (Graphics User...
  • spring123666
  • spring123666
  • 2013年09月09日 00:20
  • 3693

Java学习12 GUI程序设计(AWT)

GUI设计、GUI事件处理、GUI常用组件和视觉控制、AWT绘图、Applet。参考v512工作室培训视频,请勿用于盈利目的。...
  • rayluoluo4
  • rayluoluo4
  • 2017年11月22日 17:48
  • 71

JAVA J2SE SWING AWT 登录界面 输入用户名和密码 登录器

import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; impo...
  • linghao00
  • linghao00
  • 2011年10月15日 14:15
  • 7522

《Java7程序设计》第15章 Swing基础知识

1.抽象窗口工具包AWT 2.Swing(JFC Java基本类的一部分) UI组件:顶层容器JFrame,JDialog,以及可以添加到容器中的组件。 布局管理器:如何在容器中布置组件。...
  • lsx_123_
  • lsx_123_
  • 2014年11月30日 00:04
  • 412

【JAVA语言程序设计基础篇】--图形用户界面基础--Swing GUI组件的公共特性

package chapter12; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import ...
  • qq_24653023
  • qq_24653023
  • 2016年08月03日 13:25
  • 12556

java实验之swing图形用户界面程序设计及总结

8.1组件和容器 顶层容器有小应用程序(Applet和JApplet)、对话框(Dialog和JDialog)、框架(Frame和JFrame)。这些容器都存在于java.swing*;中. ...
  • YSWLOVEHZM
  • YSWLOVEHZM
  • 2014年11月29日 00:00
  • 1549

JAVA之Swing程序设计

一.Swing概述 二.创建窗体 三.常用组件             1.JLabel标签组件             2.JButton按钮组件             3.JRadioButto...
  • slqSLQSHILIQIANG
  • slqSLQSHILIQIANG
  • 2017年05月08日 16:45
  • 263

Java程序设计基本知识

第一章   Java语言概述 1.      对象的三个特性:封装、继承和多态。 封装:把对象的设计者和使用者分开, 访问级别:public, protected, default, private(...
  • u013058160
  • u013058160
  • 2016年05月08日 00:44
  • 996
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java程序设计中密码屏蔽(awt/swing/command line)
举报原因:
原因补充:

(最多只允许输入30个字)