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'。

步骤运行:

  1. 运行 DemoWindow一次。点击“登录!”然后在对话框中“确定”。将发生错误并创建用户数据库文件 user-database.txt
  2. 运行 PasswordHasher,并生成一个散列。将该散列传递到用户数据库文件中。
  3. 运行 再次演示Window。这次玩了几个东西,比如没有用户名,没有密码,用户名错误,密码错误,正确的东西。