Java Swing 登录 window 未关闭
Java Swing login window not closing
所以我是 java 的新手,我正在尝试登录 window 作为程序的前身。登录 window 非常标准,它有一个密码和用户输入,一个登录按钮,右上角有一个 x。但是,我试图做到这一点,以便用户在输入正确的登录数据之前无法与主程序交互。然而,虽然 setenabled(false) 函数允许我阻止访问主程序,但在用户输入正确的信息后,什么也没有发生,登录屏幕仍然存在。
在我的登录中 class 我有一个登录布尔值,如果检测到正确的输入,则该值为真。但是,setenabled(true) 似乎没有执行,并且在输入正确的输入后登录 window 似乎没有关闭。这是我登录时执行的操作方法 class:
有人知道这是为什么吗?是我的 this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);没有做我认为应该做的事?它不只是关闭当前window(登录window)从而让用户访问主程序吗?
package Whosebug;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class ExitFrame {
public static void main(final String[] args) {
final JFrame f = new JFrame();
// this will do nothing, thus not react to the user trying to close the window
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
// this will only hide the window, as if fsetVisible(false) was called
f.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
// this will actually dispose the window, as if f.dispose() was called
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// this will shut down the whole JVM, ending your program, as if System.exit(0); was called
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
帮助您解决实际问题:
为了阻止对调用(父)的访问Window,您应该使用对话框,如 JDialog,将其设置为模态模式,并给父 window was 参数。
final JDialog d = new JDialog(f);
d.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
d.setModal(true);
// add elements
d.setVisible(true); // will block until d gets hidden or disposed.
// code will resume here
您也可以只使用 class MyDialog extends JDialog
,就像在代码中使用 JFrame 一样。
更高级的方法
不是在 JFrames 或 JDialogs 上实现东西,而只是作为 JPanel。为什么?因为您可以将它添加到任何您喜欢的地方,而不必担心 'external' 行为。
然后您可以将该面板添加到 JFrame、JWindow、JDialog、另一个 JPanel,甚至是带有消息对话框的 JOptionPane 等。
实施起来有点困难,因为您需要以某种方式将父级的 'external' 事件附加到 JPanel,但是一旦您弄明白了,它就很容易了。
完整程序示例:
我刚刚在 Swing 中实现了一个登录演示,向您展示我将如何做。这适用于这个问题和另一个问题,我将 post 一个 link 到这里。
演示Window
package Whosebug.userlogin;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import Whosebug.userlogin.UserLoginPanel.UserCredentials;
public class DemoWindow extends JFrame {
private static final long serialVersionUID = -5431874284425397696L;
public static void main(final String[] args) {
new DemoWindow();
}
private final JTextField gTxtStatus = new JTextField();
private final JButton gBtnLogin = new JButton("Log in!");
private User mCurrentUser = null;
public DemoWindow() {
setTitle("Logging in...");
setBounds(100, 100, 200, 200);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
add(new JLabel("Status:"), BorderLayout.NORTH);
gTxtStatus.setText("Not logged in!");
add(gTxtStatus);
gBtnLogin.addActionListener(al -> runLogin());
add(gBtnLogin, BorderLayout.SOUTH);
setVisible(true);
}
private void runLogin() {
try {
UserCredentials uc = null;
while (true) {
uc = UserLoginPanel.getUserCredentials(this, uc);
if (uc == null) {
gTxtStatus.setText("User login aborted.");
break;
}
final User user = UserManager.getUserByName(uc.mUsername);
if (user == null) {
gTxtStatus.setText("No user with name [" + uc.mUsername + "] found!");
continue;
}
final String hashedPassword = PasswordHasher.hashPassword(uc.mPasswordChars);
final boolean passwordOK = user.matchesPasswordHash(hashedPassword);
if (!passwordOK) {
gTxtStatus.setText("Password mismatch!");
continue;
}
mCurrentUser = user;
gTxtStatus.setText("User logged in: [" + mCurrentUser + "]");
break;
}
} catch (final Exception e) {
JOptionPane.showMessageDialog(this, "Error: " + e, "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
密码哈希
package Whosebug.userlogin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
public class PasswordHasher {
static public final String MY_APP_SALT = PasswordHasher.class.getPackageName();
public static void main(final String[] args) throws IOException {
try (final BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));) {
while (true) {
System.out.println("Enter password. Enter empy line to end app.");
final String line = buffer.readLine();
if (line == null || line.trim().length() < 1) break;
final String hash = hashPassword(line.toCharArray());
System.out.println("Password: " + line);
System.out.println("Hash: " + hash);
System.out.println();
}
}
System.out.println("\nApp ended.");
}
static public String hashPassword(final char[] pPasswordChars) {
try {
final MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
final byte[] saltBuffer = MY_APP_SALT.getBytes("UTF-8");
md.update(saltBuffer);
final byte[] passwordBuffer = charsToBytes(pPasswordChars);
md.update(passwordBuffer);
final byte[] digest = md.digest();
String hexStr = "";
for (int i = 0; i < digest.length; i++) {
hexStr += Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1);
}
return hexStr;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
static public byte[] charsToBytes(final char[] pChars) {
final CharBuffer charBuffer = CharBuffer.wrap(pChars);
final ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
final byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return bytes;
}
}
用户
package Whosebug.userlogin;
import java.util.Objects;
public class User {
private final String mUsername;
private final String mPasswordHash;
public User(final String pUsername, final String pPasswordHash) {
mUsername = pUsername;
mPasswordHash = pPasswordHash;
}
public User(final String pLine) {
final String[] args = pLine.split(",");
mUsername = args[0];
mPasswordHash = args[1];
}
public String getUsername() {
return mUsername;
}
public boolean matchesUsername(final String pUsername) {
return stringMatches(mUsername, pUsername);
}
public boolean matchesPasswordHash(final String pPasswordHash) {
return stringMatches(mPasswordHash, pPasswordHash);
}
static public boolean stringMatches(final String pString1, final String pString2) {
final String s1 = pString1 == null ? null : pString1.trim().toLowerCase();
final String s2 = pString2 == null ? null : pString2.trim().toLowerCase();
return Objects.equals(s1, s2);
}
@Override public String toString() {
return mUsername;
}
}
用户登录面板
package Whosebug.userlogin;
import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
public class UserLoginPanel extends JPanel {
private static final long serialVersionUID = -2478650783143301888L;
static public class UserCredentials {
public final String mUsername;
public final char[] mPasswordChars;
public UserCredentials(final String pUsername, final char[] pPasswordChar) {
mUsername = pUsername;
mPasswordChars = pPasswordChar;
}
public void clearPassword() {
for (int i = 0; i < mPasswordChars.length; i++) {
mPasswordChars[i] = 0;
}
}
}
static public UserCredentials getUserCredentials(final Window pParent, final UserCredentials pDefaultCredentials) {
final JDialog d = new JDialog(pParent);
d.setTitle("Please log in");
final UserLoginPanel panel = new UserLoginPanel(ae -> d.dispose(), pDefaultCredentials);
d.setModal(true);
d.add(panel);
d.pack();
d.setVisible(true);
d.dispose();
return panel.getReturnValue();
}
private final JTextField gTxtUsername = new JTextField();
private final JPasswordField gTxtPassword = new JPasswordField();
private final JButton gBtnCancel = new JButton("Cancel");
private final JButton gBtnOK = new JButton("OK");
private final ActionListener mActionListener;
private UserCredentials mReturnValue = null;
public UserLoginPanel(final ActionListener pActionListener, final UserCredentials pDefaultCredentials) {
mActionListener = pActionListener;
mReturnValue = pDefaultCredentials;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JLabel("Username:"));
if (mReturnValue != null) gTxtUsername.setText(mReturnValue.mUsername);
add(gTxtUsername);
add(new JLabel("Password:"));
if (mReturnValue != null) gTxtPassword.setText(new String(mReturnValue.mPasswordChars));
add(gTxtPassword);
{
final JPanel hor = new JPanel();
gBtnCancel.addActionListener(al -> gBtnCancel_click());
hor.add(gBtnCancel, BorderLayout.WEST);
gBtnOK.addActionListener(al -> gBtnOK_click());
hor.add(gBtnOK, BorderLayout.EAST);
add(hor);
}
}
private void gBtnOK_click() {
mReturnValue = new UserCredentials(gTxtUsername.getText(), gTxtPassword.getPassword());
mActionListener.actionPerformed(new ActionEvent(this, 0, "ok"));
}
private void gBtnCancel_click() {
mReturnValue = null;
mActionListener.actionPerformed(new ActionEvent(this, -1, "cancel"));
}
public UserCredentials getReturnValue() {
return mReturnValue;
}
}
用户管理器
package Whosebug.userlogin;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
public class UserManager {
static public final String USER_DB_FILENAME = "user-database.txt";
static private List<User> sLoadedUsers;
static public List<User> getAllUsers() throws IOException {
if (sLoadedUsers == null) {
final Path file = Paths.get(USER_DB_FILENAME);
try {
sLoadedUsers = Files.lines(file)
.filter(p -> p != null && !p.isEmpty() && p.contains(","))
.map(p -> new User(p))
.collect(Collectors.toList());
} catch (final Exception e) {
Files.write(file, "username,passwordhash".getBytes()); // create default file
throw e;
}
}
return sLoadedUsers;
}
static public User getUserByName(final String pUsername) throws IOException {
for (final User user : getAllUsers()) {
if (user.matchesUsername(pUsername)) return user;
}
return null;
}
}
我的 user-database.txt
文件:
Peter,744ed63cee96b0542fcde52b63410ef6a9b8ae63
包含密码为 'Lustig' 的用户 'Peter'。
步骤运行:
- 运行 DemoWindow一次。点击“登录!”然后在对话框中“确定”。将发生错误并创建用户数据库文件
user-database.txt
。
- 运行 PasswordHasher,并生成一个散列。将该散列传递到用户数据库文件中。
- 运行 再次演示Window。这次玩了几个东西,比如没有用户名,没有密码,用户名错误,密码错误,正确的东西。
所以我是 java 的新手,我正在尝试登录 window 作为程序的前身。登录 window 非常标准,它有一个密码和用户输入,一个登录按钮,右上角有一个 x。但是,我试图做到这一点,以便用户在输入正确的登录数据之前无法与主程序交互。然而,虽然 setenabled(false) 函数允许我阻止访问主程序,但在用户输入正确的信息后,什么也没有发生,登录屏幕仍然存在。
在我的登录中 class 我有一个登录布尔值,如果检测到正确的输入,则该值为真。但是,setenabled(true) 似乎没有执行,并且在输入正确的输入后登录 window 似乎没有关闭。这是我登录时执行的操作方法 class:
有人知道这是为什么吗?是我的 this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);没有做我认为应该做的事?它不只是关闭当前window(登录window)从而让用户访问主程序吗?
package Whosebug;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class ExitFrame {
public static void main(final String[] args) {
final JFrame f = new JFrame();
// this will do nothing, thus not react to the user trying to close the window
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
// this will only hide the window, as if fsetVisible(false) was called
f.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
// this will actually dispose the window, as if f.dispose() was called
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// this will shut down the whole JVM, ending your program, as if System.exit(0); was called
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
帮助您解决实际问题:
为了阻止对调用(父)的访问Window,您应该使用对话框,如 JDialog,将其设置为模态模式,并给父 window was 参数。
final JDialog d = new JDialog(f);
d.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
d.setModal(true);
// add elements
d.setVisible(true); // will block until d gets hidden or disposed.
// code will resume here
您也可以只使用 class MyDialog extends JDialog
,就像在代码中使用 JFrame 一样。
更高级的方法
不是在 JFrames 或 JDialogs 上实现东西,而只是作为 JPanel。为什么?因为您可以将它添加到任何您喜欢的地方,而不必担心 'external' 行为。
然后您可以将该面板添加到 JFrame、JWindow、JDialog、另一个 JPanel,甚至是带有消息对话框的 JOptionPane 等。
实施起来有点困难,因为您需要以某种方式将父级的 'external' 事件附加到 JPanel,但是一旦您弄明白了,它就很容易了。
完整程序示例:
我刚刚在 Swing 中实现了一个登录演示,向您展示我将如何做。这适用于这个问题和另一个问题,我将 post 一个 link 到这里。
演示Window
package Whosebug.userlogin;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import Whosebug.userlogin.UserLoginPanel.UserCredentials;
public class DemoWindow extends JFrame {
private static final long serialVersionUID = -5431874284425397696L;
public static void main(final String[] args) {
new DemoWindow();
}
private final JTextField gTxtStatus = new JTextField();
private final JButton gBtnLogin = new JButton("Log in!");
private User mCurrentUser = null;
public DemoWindow() {
setTitle("Logging in...");
setBounds(100, 100, 200, 200);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
add(new JLabel("Status:"), BorderLayout.NORTH);
gTxtStatus.setText("Not logged in!");
add(gTxtStatus);
gBtnLogin.addActionListener(al -> runLogin());
add(gBtnLogin, BorderLayout.SOUTH);
setVisible(true);
}
private void runLogin() {
try {
UserCredentials uc = null;
while (true) {
uc = UserLoginPanel.getUserCredentials(this, uc);
if (uc == null) {
gTxtStatus.setText("User login aborted.");
break;
}
final User user = UserManager.getUserByName(uc.mUsername);
if (user == null) {
gTxtStatus.setText("No user with name [" + uc.mUsername + "] found!");
continue;
}
final String hashedPassword = PasswordHasher.hashPassword(uc.mPasswordChars);
final boolean passwordOK = user.matchesPasswordHash(hashedPassword);
if (!passwordOK) {
gTxtStatus.setText("Password mismatch!");
continue;
}
mCurrentUser = user;
gTxtStatus.setText("User logged in: [" + mCurrentUser + "]");
break;
}
} catch (final Exception e) {
JOptionPane.showMessageDialog(this, "Error: " + e, "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
密码哈希
package Whosebug.userlogin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
public class PasswordHasher {
static public final String MY_APP_SALT = PasswordHasher.class.getPackageName();
public static void main(final String[] args) throws IOException {
try (final BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));) {
while (true) {
System.out.println("Enter password. Enter empy line to end app.");
final String line = buffer.readLine();
if (line == null || line.trim().length() < 1) break;
final String hash = hashPassword(line.toCharArray());
System.out.println("Password: " + line);
System.out.println("Hash: " + hash);
System.out.println();
}
}
System.out.println("\nApp ended.");
}
static public String hashPassword(final char[] pPasswordChars) {
try {
final MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
final byte[] saltBuffer = MY_APP_SALT.getBytes("UTF-8");
md.update(saltBuffer);
final byte[] passwordBuffer = charsToBytes(pPasswordChars);
md.update(passwordBuffer);
final byte[] digest = md.digest();
String hexStr = "";
for (int i = 0; i < digest.length; i++) {
hexStr += Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1);
}
return hexStr;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
static public byte[] charsToBytes(final char[] pChars) {
final CharBuffer charBuffer = CharBuffer.wrap(pChars);
final ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
final byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return bytes;
}
}
用户
package Whosebug.userlogin;
import java.util.Objects;
public class User {
private final String mUsername;
private final String mPasswordHash;
public User(final String pUsername, final String pPasswordHash) {
mUsername = pUsername;
mPasswordHash = pPasswordHash;
}
public User(final String pLine) {
final String[] args = pLine.split(",");
mUsername = args[0];
mPasswordHash = args[1];
}
public String getUsername() {
return mUsername;
}
public boolean matchesUsername(final String pUsername) {
return stringMatches(mUsername, pUsername);
}
public boolean matchesPasswordHash(final String pPasswordHash) {
return stringMatches(mPasswordHash, pPasswordHash);
}
static public boolean stringMatches(final String pString1, final String pString2) {
final String s1 = pString1 == null ? null : pString1.trim().toLowerCase();
final String s2 = pString2 == null ? null : pString2.trim().toLowerCase();
return Objects.equals(s1, s2);
}
@Override public String toString() {
return mUsername;
}
}
用户登录面板
package Whosebug.userlogin;
import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
public class UserLoginPanel extends JPanel {
private static final long serialVersionUID = -2478650783143301888L;
static public class UserCredentials {
public final String mUsername;
public final char[] mPasswordChars;
public UserCredentials(final String pUsername, final char[] pPasswordChar) {
mUsername = pUsername;
mPasswordChars = pPasswordChar;
}
public void clearPassword() {
for (int i = 0; i < mPasswordChars.length; i++) {
mPasswordChars[i] = 0;
}
}
}
static public UserCredentials getUserCredentials(final Window pParent, final UserCredentials pDefaultCredentials) {
final JDialog d = new JDialog(pParent);
d.setTitle("Please log in");
final UserLoginPanel panel = new UserLoginPanel(ae -> d.dispose(), pDefaultCredentials);
d.setModal(true);
d.add(panel);
d.pack();
d.setVisible(true);
d.dispose();
return panel.getReturnValue();
}
private final JTextField gTxtUsername = new JTextField();
private final JPasswordField gTxtPassword = new JPasswordField();
private final JButton gBtnCancel = new JButton("Cancel");
private final JButton gBtnOK = new JButton("OK");
private final ActionListener mActionListener;
private UserCredentials mReturnValue = null;
public UserLoginPanel(final ActionListener pActionListener, final UserCredentials pDefaultCredentials) {
mActionListener = pActionListener;
mReturnValue = pDefaultCredentials;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JLabel("Username:"));
if (mReturnValue != null) gTxtUsername.setText(mReturnValue.mUsername);
add(gTxtUsername);
add(new JLabel("Password:"));
if (mReturnValue != null) gTxtPassword.setText(new String(mReturnValue.mPasswordChars));
add(gTxtPassword);
{
final JPanel hor = new JPanel();
gBtnCancel.addActionListener(al -> gBtnCancel_click());
hor.add(gBtnCancel, BorderLayout.WEST);
gBtnOK.addActionListener(al -> gBtnOK_click());
hor.add(gBtnOK, BorderLayout.EAST);
add(hor);
}
}
private void gBtnOK_click() {
mReturnValue = new UserCredentials(gTxtUsername.getText(), gTxtPassword.getPassword());
mActionListener.actionPerformed(new ActionEvent(this, 0, "ok"));
}
private void gBtnCancel_click() {
mReturnValue = null;
mActionListener.actionPerformed(new ActionEvent(this, -1, "cancel"));
}
public UserCredentials getReturnValue() {
return mReturnValue;
}
}
用户管理器
package Whosebug.userlogin;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
public class UserManager {
static public final String USER_DB_FILENAME = "user-database.txt";
static private List<User> sLoadedUsers;
static public List<User> getAllUsers() throws IOException {
if (sLoadedUsers == null) {
final Path file = Paths.get(USER_DB_FILENAME);
try {
sLoadedUsers = Files.lines(file)
.filter(p -> p != null && !p.isEmpty() && p.contains(","))
.map(p -> new User(p))
.collect(Collectors.toList());
} catch (final Exception e) {
Files.write(file, "username,passwordhash".getBytes()); // create default file
throw e;
}
}
return sLoadedUsers;
}
static public User getUserByName(final String pUsername) throws IOException {
for (final User user : getAllUsers()) {
if (user.matchesUsername(pUsername)) return user;
}
return null;
}
}
我的 user-database.txt
文件:
Peter,744ed63cee96b0542fcde52b63410ef6a9b8ae63
包含密码为 'Lustig' 的用户 'Peter'。
步骤运行:
- 运行 DemoWindow一次。点击“登录!”然后在对话框中“确定”。将发生错误并创建用户数据库文件
user-database.txt
。 - 运行 PasswordHasher,并生成一个散列。将该散列传递到用户数据库文件中。
- 运行 再次演示Window。这次玩了几个东西,比如没有用户名,没有密码,用户名错误,密码错误,正确的东西。