本專案展示如何使用 Java Swing 構建一個功能完整的桌面應用登錄註冊系統,包含用戶註冊、登錄驗證、圖形驗證碼、輸入限制與實時提示等核心功能。技術棧包括:
CREATE DATABASE db_database01 CHARACTER SET utf8mb4;
USE db_database01;
CREATE TABLE tb_user (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(15) NOT NULL UNIQUE,
password VARCHAR(20) NOT NULL,
email VARCHAR(45) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 初始測試數據
INSERT INTO tb_user (username, password, email) VALUES ('demo', 'demo123', 'demo@example.com');
// User.java - 實體類
public class User {
private int id;
private String username;
private String password;
private String email;
// getter/setter 省略
}
// DBConfig.java - 數據庫配置接口
public interface DBConfig {
String URL = "jdbc:mysql://localhost:3306/db_database01?useSSL=false&serverTimezone=UTC";
String USER = "root";
String PASSWORD = "your_password";
}
// DBHelper.java - 核心數據操作類
public class DBHelper implements DBConfig {
private static final QueryRunner RUNNER = new QueryRunner();
private static final MysqlDataSource DATA_SOURCE = new MysqlDataSource();
static {
DATA_SOURCE.setURL(URL);
DATA_SOURCE.setUser(USER);
DATA_SOURCE.setPassword(PASSWORD);
}
public static Connection getConnection() throws SQLException {
return DATA_SOURCE.getConnection();
}
// 用戶名唯一性檢查
public static boolean exists(String username) {
String sql = "SELECT 1 FROM tb_user WHERE username = ?";
try (Connection conn = getConnection()) {
Long count = RUNNER.query(conn, sql, new ScalarHandler<Long>(), username);
return count != null && count > 0;
} catch (SQLException e) {
throw new RuntimeException("檢查用戶名失敗", e);
}
}
// 登錄驗證
public static boolean check(String username, char[] password) {
String sql = "SELECT password FROM tb_user WHERE username = ?";
try (Connection conn = getConnection()) {
String dbPwd = RUNNER.query(conn, sql, new ScalarHandler<String>(), username);
if (dbPwd == null) return false;
boolean match = Arrays.equals(password, dbPwd.toCharArray());
Arrays.fill(password, '0'); // 清除內存中的明文密碼
return match;
} catch (SQLException e) {
throw new RuntimeException("驗證失敗", e);
}
}
// 註冊保存
public static boolean save(User user) {
String sql = "INSERT INTO tb_user (username, password, email) VALUES (?, ?, ?)";
try (Connection conn = getConnection()) {
return RUNNER.update(conn, sql, user.getUsername(), user.getPassword(), user.getEmail()) > 0;
} catch (SQLException e) {
throw new RuntimeException("保存用戶失敗", e);
}
}
}
自定義 JLabel 子類實現驗證碼繪製,支援單色與彩色兩種模式:
// 基礎驗證碼標籤
public class CaptchaLabel extends JLabel {
private final String text;
public CaptchaLabel(String text) {
this.text = text;
setPreferredSize(new Dimension(80, 36));
setFont(new Font("微軟雅黑", Font.BOLD, 20));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawString(text, 10, 26);
}
}
// 彩色干擾線驗證碼
public class ColorfulCaptchaLabel extends JLabel {
private final String text;
private static final Color[] COLORS = {Color.RED, Color.BLUE, Color.GREEN, Color.MAGENTA, Color.ORANGE};
public ColorfulCaptchaLabel(String text) {
this.text = text;
setPreferredSize(new Dimension(100, 40));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 繪製干擾線
Random rand = new Random();
for (int i = 0; i < 10; i++) {
g2d.setColor(COLORS[rand.nextInt(COLORS.length)]);
g2d.drawLine(rand.nextInt(getWidth()), rand.nextInt(getHeight()),
rand.nextInt(getWidth()), rand.nextInt(getHeight()));
}
// 逐字符繪製,顏色隨機
for (int i = 0; i < text.length(); i++) {
g2d.setColor(COLORS[rand.nextInt(COLORS.length)]);
g2d.setFont(new Font("微軟雅黑", Font.BOLD, 24));
g2d.drawString(String.valueOf(text.charAt(i)), 15 + i * 20, 30);
}
}
}
使用 DocumentFilter 限制輸入長度,DocumentListener 實時顯示字符計數:
// 長度限制過濾器
public class LengthFilter extends DocumentFilter {
private final int maxLength;
public LengthFilter(int maxLength) {
this.maxLength = maxLength;
}
@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
if (fb.getDocument().getLength() + string.length() <= maxLength) {
super.insertString(fb, offset, string, attr);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
if (fb.getDocument().getLength() + text.length() - length <= maxLength) {
super.replace(fb, offset, length, text, attrs);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
}
// 實時提示監聽器
public class LengthListener implements DocumentListener {
private final JLabel tipLabel;
private final int maxLength;
public LengthListener(JLabel tipLabel, int maxLength) {
this.tipLabel = tipLabel;
this.maxLength = maxLength;
}
@Override
public void insertUpdate(DocumentEvent e) { updateTip(e); }
@Override
public void removeUpdate(DocumentEvent e) { updateTip(e); }
@Override
public void changedUpdate(DocumentEvent e) { updateTip(e); }
private void updateTip(DocumentEvent e) {
int len = e.getDocument().getLength();
tipLabel.setText(len + "/" + maxLength);
tipLabel.setForeground(len > maxLength * 0.8 ? Color.RED : Color.BLACK);
}
}
public class LoginFrame extends JFrame {
private JTextField usernameField;
private JPasswordField passwordField;
private JTextField captchaField;
private final String captchaText = RandomStringUtils.randomAlphanumeric(4);
public LoginFrame() {
setTitle("系統登錄");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
// 用戶名面板
add(createLabeledPanel("用戶名:", usernameField = new JTextField(15)));
// 密碼面板
add(createLabeledPanel("密碼:", passwordField = new JPasswordField(15)));
// 驗證碼面板
JPanel captchaPanel = new JPanel();
captchaPanel.add(new JLabel("驗證碼:"));
captchaPanel.add(captchaField = new JTextField(5));
captchaPanel.add(new ColorfulCaptchaLabel(captchaText));
add(captchaPanel);
// 按鈕面板
JPanel btnPanel = new JPanel();
JButton loginBtn = new JButton("登錄");
loginBtn.addActionListener(this::doLogin);
JButton cancelBtn = new JButton("取消");
cancelBtn.addActionListener(e -> System.exit(0));
btnPanel.add(loginBtn);
btnPanel.add(cancelBtn);
add(btnPanel);
pack();
setLocationRelativeTo(null);
}
private void doLogin(ActionEvent e) {
String username = usernameField.getText().trim();
char[] password = passwordField.getPassword();
String captcha = captchaField.getText().trim();
// 非空校驗
if (username.isEmpty() || password.length == 0 || captcha.isEmpty()) {
showWarning("所有字段均不能為空");
return;
}
// 驗證碼校驗
if (!captcha.equalsIgnoreCase(captchaText)) {
showWarning("驗證碼錯誤");
return;
}
// 數據庫驗證
if (!DBHelper.exists(username)) {
showWarning("用戶不存在");
return;
}
if (!DBHelper.check(username, password)) {
showWarning("密碼錯誤");
return;
}
// 登錄成功,打開主窗體
SwingUtilities.invokeLater(() -> {
new MainFrame().setVisible(true);
dispose();
});
}
private JPanel createLabeledPanel(String labelText, JComponent field) {
JPanel panel = new JPanel();
JLabel label = new JLabel(labelText);
label.setFont(new Font("微軟雅黑", Font.PLAIN, 14));
field.setFont(new Font("微軟雅黑", Font.PLAIN, 14));
panel.add(label);
panel.add(field);
return panel;
}
private void showWarning(String msg) {
JOptionPane.showMessageDialog(this, msg, "提示", JOptionPane.WARNING_MESSAGE);
}
}
public class RegisterFrame extends JFrame {
// 組件聲明...
private void doRegister(ActionEvent e) {
String username = usernameField.getText().trim();
char[] pwd1 = passwordField1.getPassword();
char[] pwd2 = passwordField2.getPassword();
String email = emailField.getText().trim();
// 非空校驗
if (username.isEmpty() || pwd1.length == 0 || pwd2.length == 0 || email.isEmpty()) {
showWarning("所有字段均不能為空");
return;
}
// 正則驗證
if (!username.matches("\\w{5,15}")) {
showWarning("用戶名需 5-15 位字母數字下劃線");
return;
}
if (!Arrays.equals(pwd1, pwd2)) {
showWarning("兩次密碼不一致");
return;
}
if (!email.matches("^[\\w.-]+@[\\w.-]+\\.\\w+$")) {
showWarning("郵箱格式不正確");
return;
}
if (DBHelper.exists(username)) {
showWarning("用戶名已被佔用");
return;
}
// 保存用戶
User user = new User();
user.setUsername(username);
user.setPassword(new String(pwd1));
user.setEmail(email);
Arrays.fill(pwd1, '0');
Arrays.fill(pwd2, '0');
if (DBHelper.save(user)) {
JOptionPane.showMessageDialog(this, "註冊成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
dispose();
} else {
showWarning("註冊失敗,請重試");
}
}
}
DBConfig 中的數據庫連接參數mysql-connector-java、commons-dbutils、commons-lang3、commons-codecLoginFrame.main() 啟動應用這個登錄註冊模組雖然基於 Swing 這一經典框架,但涵蓋了桌面應用開發的核心要素:分層架構、輸入校驗、安全處理、用戶體驗細節。理解並掌握這些基礎,對於後續學習現代 GUI 框架或全棧開發都大有裨益。