JOptionPane для получения пароля

JOptionPane можно использовать для получения строковых вводов от пользователя, но в моем случае я хочу отобразить поле пароля в showInputDialog.

Мне нужно, чтобы ввод данных пользователем был замаскирован, а возвращаемое значение должно быть в char[]. Мне нужно диалоговое окно с сообщением, полем пароля и двумя кнопками. Это можно сделать? Спасибо.


person Ahamed    schedule 16.01.2012    source источник


Ответы (6)


Да, это возможно с помощью JOptionPane.showOptionDialog(). Что-то вроде этого:

JPanel panel = new JPanel();
JLabel label = new JLabel("Enter a password:");
JPasswordField pass = new JPasswordField(10);
panel.add(label);
panel.add(pass);
String[] options = new String[]{"OK", "Cancel"};
int option = JOptionPane.showOptionDialog(null, panel, "The title",
                         JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
                         null, options, options[1]);
if(option == 0) // pressing OK button
{
    char[] password = pass.getPassword();
    System.out.println("Your password is: " + new String(password));
}
person Eng.Fouad    schedule 16.01.2012
comment
JPasswordField(10) не принимает пароли длиннее 10. Лучше сделать это намного шире или использовать конструктор без аргументов, такой как @Adamski ниже. Также OK должен быть параметром по умолчанию, потому что многие пользователи по привычке нажимают Enter после ввода пароля. Если значение «Отмена» установлено по умолчанию, то ввод пароля с последующим вводом приведет к отмене диалогового окна. - person gb96; 21.12.2012
comment
это хорошая метка с JPasswordField в JOptionPane, и в моем случае она принимает пароль длиннее 10. - person Sagar G.; 05.07.2013
comment
Как выделить поле пароля при появлении диалогового окна? - person mjaggard; 05.05.2015
comment
Это не работает. вариант == 1, когда я нажимаю Enter в поле пароля. - person Stefan Reich; 20.06.2016
comment
передайте options[0], чтобы сделать OK значением по умолчанию. Это вернет option==0, когда вы нажмете Enter в поле пароля. - person Ekkelenkamp; 18.06.2020

Проще всего использовать JOptionPane showConfirmDialog и передать ссылку на JPasswordField; например

JPasswordField pf = new JPasswordField();
int okCxl = JOptionPane.showConfirmDialog(null, pf, "Enter Password", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);

if (okCxl == JOptionPane.OK_OPTION) {
  String password = new String(pf.getPassword());
  System.err.println("You entered: " + password);
}

Изменить

Ниже приведен пример использования пользовательского JPanel для отображения сообщения вместе с JPasswordField. Согласно последнему комментарию, я также (наспех) добавил код, позволяющий JPasswordField получать фокус при первом отображении диалогового окна.

public class PasswordPanel extends JPanel {
  private final JPasswordField passwordField = new JPasswordField(12);
  private boolean gainedFocusBefore;

  /**
   * "Hook" method that causes the JPasswordField to request focus the first time this method is called.
   */
  void gainedFocus() {
    if (!gainedFocusBefore) {
      gainedFocusBefore = true;
      passwordField.requestFocusInWindow();
    }
  }

  public PasswordPanel() {
    super(new FlowLayout());

    add(new JLabel("Password: "));
    add(passwordField);
  }

  public char[] getPassword() {
      return passwordField.getPassword();
  }

  public static void main(String[] args) {
      PasswordPanel pPnl = new PasswordPanel();
      JOptionPane op = new JOptionPane(pPnl, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);

      JDialog dlg = op.createDialog("Who Goes There?");

      // Wire up FocusListener to ensure JPasswordField is able to request focus when the dialog is first shown.
      dlg.addWindowFocusListener(new WindowAdapter() {
        @Override
        public void windowGainedFocus(WindowEvent e) {
            pPnl.gainedFocus();
        }
      });

      if (op.getValue() != null && op.getValue().equals(JOptionPane.OK_OPTION)) {
          String password = new String(pPnl.getPassword());
          System.err.println("You entered: " + password);
      }
  }
}
person Adamski    schedule 16.01.2012
comment
Как раз то, что я искал. Кстати, есть ли способ установить строку сообщения/тела этого диалогового окна? - person InamTaj; 19.08.2014
comment
Самый простой способ создать JPanel, который содержит как JPasswordField, так и JLabel, содержащий нужный текст. - person Adamski; 20.08.2014
comment
Я это уже знаю, но я не хочу открывать другой фрейм во время работы приложения. Поэтому я хотел использовать ваше решение с строкой сообщения. - person InamTaj; 21.08.2014
comment
Я не уверен, что вы имеете в виду. открытие другого фрейма: пользовательскую панель JPanel можно передать вызову JOptionPane.showConfirmDialog в качестве сообщения. Смотрите мое последнее редактирование. - person Adamski; 28.08.2014
comment
Работал отлично. Спасибо! - person InamTaj; 28.08.2014
comment
Это хорошо, но, к сожалению, поле пароля не имеет фокуса, и для начала ввода требуется дополнительный щелчок мышью. - person Adam Mackler; 11.09.2014
comment
@AdamMackler: я изменил код, чтобы учесть это. - person Adamski; 12.09.2014
comment
@ Адамски Спасибо. При построении JOptionPanel я просто заменил selectInitialValue() на { passwordField.requestFocusInWindow() }. Непонятно, почему вы избегаете вызова requestFocusInWindow() более одного раза. Кроме того, я думаю, вы написали loginPnl вместо pPnl. - person Adam Mackler; 13.09.2014
comment
@AdamMackler: Спасибо; исправил ту опечатку. Ре. ваш комментарий Я избегаю запроса фокуса более одного раза, чтобы избежать подобных ситуаций: а) Отобразить диалоговое окно пароля, поле пароля имеет фокус. б) Пользователь вводит пароль, на вкладке «ОК» он собирается закрыться, но затем ему приходится переключаться на другое приложение. c) Пользователь переключается обратно в приложение Java, диалоговое окно восстанавливает фокус, а поле пароля повторно запрашивает фокус (!), Несмотря на то, что пользователь уже ввел свой пароль. - person Adamski; 15.09.2014
comment
@Adamski Это имеет смысл, но однострочное переопределение selectInitialValue(), похоже, делает то же самое. Я не вижу преимущества 13-строчного решения, включающего новое поле, новый метод и добавление прослушивателя в диалоговое окно. - person Adam Mackler; 16.09.2014
comment
Ну, как я уже сказал, это было сделано наспех. Возможно, вам следует опубликовать свой код в качестве альтернативного решения? - person Adamski; 16.09.2014
comment
Здорово. Это потрясающе. Я использовал ваш класс в качестве основы для слегка улучшенной версии, включающей String showDialog методов. Они содержат логику, показанную в методе main, а main просто содержит вызов showDialog. - person Agi Hammerthief; 04.09.2018

Вы можете создать свой собственный диалог, который расширяет JDialog, а затем вы можете поместить в него все, что хотите.

person OnResolve    schedule 16.01.2012

Этот диалог выглядит намного лучше, если вы

dlg.setVisible(true);

Без этого вообще не видно.

Также

pPnl.gainedFocus();

должно быть

pPnl.gainedFocus();

В остальном он прекрасно работает. Спасибо за код. Сэкономил мне время, когда я столкнулся с Swing.

Кроме того, если вы не хотите, чтобы диалог работал в фоновом режиме каждый раз, когда вы его открываете, вам нужно будет закрыть его чем-то вроде

dlg.dispatchEvent(new WindowEvent(dlg, WindowEvent.WINDOW_CLOSING));
dlg.dispose(); // else java VM will wait for dialog to be disposed of (forever)
person Peter West    schedule 25.08.2015

Решение Kotlin, основанное на ответе Адамски.

На этот раз без кнопок, которые переключают фокус, просто введите пароль и нажмите Enter или Escape, чтобы отказаться от ввода:

class PasswordPanel : JPanel(FlowLayout()) {

    private val passwordField = JPasswordField(20)
    private var entered = false

    val enteredPassword
        get() = if (entered) String(passwordField.password) else ""

    init {
        add(JLabel("Password: "))
        add(passwordField)
        passwordField.setActionCommand("OK")
        passwordField.addActionListener {
            if (it.actionCommand == "OK") {
                entered = true

                // https://stackoverflow.com/a/51356151/1020871
                SwingUtilities.getWindowAncestor(it.source as JComponent)
                    .dispose()
            }
        }
    }

    private fun request(passwordIdentifier: String) = apply {
        JOptionPane.showOptionDialog(null, this@PasswordPanel,
            "Enter $passwordIdentifier",
            JOptionPane.DEFAULT_OPTION,
            JOptionPane.INFORMATION_MESSAGE,
            null, emptyArray(), null)
    }

    companion object {

        fun requestPassword(passwordIdentifier: String) = PasswordPanel()
            .request(passwordIdentifier)
            .enteredPassword
    }
}

Использование: PasswordPanel.requestPassword(passwordIdentifier)

person Love    schedule 17.03.2019

Следующий класс является расширением отличного ответа Адамски:

package com.stackoverflow.swing;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;

/**
 * Class that creates a panel with a password field. Extension of Adamski's class
 *
 * @author adamski https://stackoverflow.com/users/127479/adamski
 * @author agi-hammerthief https://stackoverflow.com/users/2225787/agi-hammerthief
 * @see https://stackoverflow.com/a/8881370/2225787
 */
public class PasswordPanel extends JPanel {

  private final JPasswordField JFieldPass;
  private JLabel JLblPass;
  private boolean gainedFocusBefore;

  /**
   * "Hook" method that causes the JPasswordField to request focus when method is
   * first  called.
   */
  public void gainedFocus () {
    if (!gainedFocusBefore) {
      gainedFocusBefore = true;
      JFieldPass.requestFocusInWindow();
    }
  }

  public PasswordPanel (int length) {
    super(new FlowLayout());
    gainedFocusBefore = false;
    JFieldPass = new JPasswordField(length);
    Dimension d = new Dimension();
    d.setSize(30, 22);
    JFieldPass.setMinimumSize(d);
    JFieldPass.setColumns(10);
    JLblPass = new JLabel("Password: ");
    add(JLblPass);
    add(JFieldPass);
  }

  public PasswordPanel() {
    super(new FlowLayout());
    gainedFocusBefore = false;
    JFieldPass = new JPasswordField();
    Dimension d = new Dimension();
    d.setSize(30, 22);
    JFieldPass.setMinimumSize(d);
    JFieldPass.setColumns(10);
    JLblPass = new JLabel("Password: ");
    add(JLblPass);
    add(JFieldPass);
  }

  public char[] getPassword() {
    return JFieldPass.getPassword();
  }

  public String getPasswordString() {
    StringBuilder passBuilder = new StringBuilder();

    char[] pwd = this.getPassword();
    if (pwd.length > 0) {
      for (char c : pwd) {
        passBuilder.append(c);
      }
    }

    return passBuilder.toString();
  }

  private static String displayDialog (
    Component parent, final PasswordPanel panel, String title
  ) {
    String password = null;
    /* For some reason, using `JOptionPane(panel, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)`
    does not give the same results as setting values after creation, which is weird */
    JOptionPane op = new JOptionPane(panel);
    op.setMessageType(JOptionPane.QUESTION_MESSAGE);
    op.setOptionType(JOptionPane.OK_CANCEL_OPTION);
    JDialog dlg = op.createDialog(parent, title);
    // Ensure the JPasswordField is able to request focus when the dialog is first shown.
    dlg.addWindowFocusListener (new WindowAdapter () {
      @Override
      public void windowGainedFocus (WindowEvent e) {
        panel.gainedFocus ();
      }
    });
    dlg.setDefaultCloseOperation (JOptionPane.OK_OPTION); // necessary?

    dlg.setVisible (true);

    Object val = op.getValue ();
    if (null != val && val.equals (JOptionPane.OK_OPTION)) {
      password = panel.getPasswordString();
    }

    return password;
  }

  public static String showDialog (Component parent, String title) {
    final PasswordPanel pPnl = new PasswordPanel();
    return displayDialog(parent, pPnl, title);
  }

  public static String showDialog (
    Component parent, String title, int passwordLength
  ) {
    final PasswordPanel pPnl = new PasswordPanel(passwordLength);

    return displayDialog (parent, pPnl, title);
  }

  public static String showDialog (String title) {
    return showDialog(null, title);
  }

  public static String showDialog (String title, int passwordLength) {
    return showDialog(null, title, passwordLength);
  }

  /**
   * Show a test dialog
   */
  public static void main(String[] args) {
    String test = PasswordPanel.showDialog ("Enter Your Password");
    System.out.println ("Entered Password: " + test);
    System.exit(0);
  }

}
person Agi Hammerthief    schedule 04.09.2018