0

Series Java Swing Bài 13: JTextField & JPasswordField: Nhập liệu văn bản và mật khẩu

Khi xây dựng form, ngoài việc hiển thị nhãn (JLabel) và nhận tương tác click (JButton), ứng dụng của bạn cần thu thập dữ liệu đầu vào từ người dùng. Swing cung cấp hai Component cơ bản nhất cho tác vụ này: JTextField (nhập chữ hiển thị) và JPasswordField (nhập chữ bị che khuất). Cả hai đều chỉ hỗ trợ nhập liệu trên một dòng duy nhất.

1. JTextField: Ô nhập văn bản thông thường

JTextField cho phép người dùng gõ văn bản tự do. Bạn có thể thiết lập số lượng cột (columns) để ước lượng chiều rộng của ô nhập.

Java

// Khởi tạo ô nhập trống, chiều rộng khoảng 20 ký tự
JTextField txtUsername = new JTextField(20);

// Khởi tạo ô nhập có sẵn giá trị mặc định
JTextField txtEmail = new JTextField("admin@example.com", 20);

Các phương thức thao tác dữ liệu cốt lõi

  • getText(): Lấy chuỗi văn bản mà người dùng đã nhập.

  • setText(String text): Đặt lại nội dung cho ô nhập (thường dùng để xóa trắng ô nhập sau khi submit: setText("")).

  • setEditable(boolean b): Nếu đặt là false, ô nhập sẽ chuyển sang trạng thái "Chỉ đọc" (Read-only). Người dùng có thể bôi đen copy chữ, nhưng không thể gõ thêm hay xóa đi. Giao diện vẫn giữ màu nền trắng.

  • setEnabled(boolean b): Nếu đặt là false, ô nhập bị vô hiệu hóa hoàn toàn, màu nền chuyển sang xám và không thể tương tác (copy/paste đều bị cấm).

2. JPasswordField: Bảo mật mật khẩu

JPasswordField là một lớp con kế thừa từ JTextField. Chức năng y hệt, nhưng nó có một "lớp mặt nạ": mọi ký tự người dùng gõ vào đều bị biến thành các dấu chấm đen • hoặc dấu sao * để tránh bị nhìn trộm.

Java

JPasswordField txtPassword = new JPasswordField(20);

// Tùy chỉnh ký tự che giấu (mặc định là dấu chấm tròn '•')
// Đổi mặt nạ thành dấu sao '*'
txtPassword.setEchoChar('*'); 

// Hiển thị lại mật khẩu (tính năng "Hiện mật khẩu" / Show password)
// Truyền ký tự null (0) để lột bỏ mặt nạ
txtPassword.setEchoChar((char) 0); 

3. Kiến thức bảo mật nâng cao: Tại sao getPassword() lại trả về char[]?

Nếu bạn từng thử lấy dữ liệu từ JPasswordField bằng hàm quen thuộc getText(), trình biên dịch Java sẽ gạch ngang hàm đó kèm cảnh báo Deprecated (lỗi thời và không an toàn). Thay vào đó, Java ép bạn phải dùng hàm getPassword().

Tại sao lại có sự khác biệt này?

  • getText() trả về đối tượng String. Trong Java, String là Immutable (không thể thay đổi). Khi chuỗi mật khẩu được tạo ra, nó sẽ nằm chết trong bộ nhớ (String Pool) một thời gian rất dài cho đến khi bộ dọn rác (Garbage Collector) dọn đi. Nếu hacker dump (trích xuất) bộ nhớ RAM, họ sẽ đọc được mật khẩu dạng rõ (plaintext).

  • getPassword() trả về mảng ký tự char[]. Mảng là cấu trúc dữ liệu có thể thay đổi. Ngay sau khi xác thực đăng nhập xong, bạn có thể chủ động ghi đè mảng này bằng các ký tự rác (ví dụ: Arrays.fill(password, '0')). Mật khẩu gốc sẽ lập tức bốc hơi khỏi thanh RAM, đảm bảo an toàn tuyệt đối.

4. Source Code Thực Chiến: Giao diện Đăng Nhập

Đoạn code dưới đây kết hợp GridBagLayout (từ Bài 10) với JTextField, JPasswordField và JCheckBox (tính năng hiện mật khẩu) để tạo ra một Form đăng nhập chuẩn mực.

Java

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;

public class LoginFrameDemo extends JFrame {

    public LoginFrameDemo() {
        setTitle("Đăng nhập hệ thống");
        setSize(400, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10, 10, 10, 10);
        gbc.fill = GridBagConstraints.HORIZONTAL;

        // ==========================================
        // Dòng 1: Username (JTextField)
        // ==========================================
        gbc.gridx = 0; gbc.gridy = 0;
        gbc.weightx = 0.0;
        add(new JLabel("Tài khoản:"), gbc);

        gbc.gridx = 1; gbc.gridy = 0;
        gbc.weightx = 1.0;
        JTextField txtUsername = new JTextField(20);
        add(txtUsername, gbc);

        // ==========================================
        // Dòng 2: Password (JPasswordField)
        // ==========================================
        gbc.gridx = 0; gbc.gridy = 1;
        gbc.weightx = 0.0;
        add(new JLabel("Mật khẩu:"), gbc);

        gbc.gridx = 1; gbc.gridy = 1;
        gbc.weightx = 1.0;
        JPasswordField txtPassword = new JPasswordField(20);
        add(txtPassword, gbc);

        // ==========================================
        // Dòng 3: Hiển thị mật khẩu (JCheckBox)
        // ==========================================
        gbc.gridx = 1; gbc.gridy = 2;
        JCheckBox chkShowPassword = new JCheckBox("Hiển thị mật khẩu");
        add(chkShowPassword, gbc);

        // Xử lý sự kiện Ẩn/Hiện mật khẩu
        chkShowPassword.addActionListener(e -> {
            if (chkShowPassword.isSelected()) {
                txtPassword.setEchoChar((char) 0); // Hiện chữ gốc
            } else {
                txtPassword.setEchoChar('•'); // Che bằng dấu chấm
            }
        });

        // ==========================================
        // Dòng 4: Nút Submit
        // ==========================================
        gbc.gridx = 1; gbc.gridy = 3;
        JButton btnLogin = new JButton("Đăng nhập");
        add(btnLogin, gbc);

        // Xử lý logic đăng nhập an toàn
        btnLogin.addActionListener(e -> {
            String username = txtUsername.getText();
            char[] password = txtPassword.getPassword(); // Trả về mảng char[]

            // Chuyển char[] thành String để so sánh (trong thực tế sẽ băm hash mảng này)
            String pwdString = new String(password); 
            
            if ("admin".equals(username) && "123456".equals(pwdString)) {
                JOptionPane.showMessageDialog(this, "Đăng nhập thành công!");
            } else {
                JOptionPane.showMessageDialog(this, "Sai tài khoản hoặc mật khẩu!", "Lỗi", JOptionPane.ERROR_MESSAGE);
            }

            // BẢO MẬT: Xóa dữ liệu mật khẩu khỏi RAM ngay sau khi xử lý xong
            Arrays.fill(password, '0');
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LoginFrameDemo().setVisible(true));
    }
}


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí