項目結構概覽
com.example.auth
├── entity
│ └── User.java # 用戶實體類
├── dao
│ ├── UserDao.java # 數據訪問接口
│ └── UserDaoImpl.java # JDBC 實現
├── service
│ ├── AuthService.java # 業務邏輯接口
│ └── AuthServiceImpl.java # 註冊/登錄邏輯
└── Main.java # 控制台測試入口
1. 實體類 User
package com.example.auth.entity;
public class User {
private String name; // 顯示名
private String loginName; // 登錄賬號
private String password; // 密碼(實際應存儲加密摘要)
// Getter/Setter/構造器/toString 省略
}
2. 數據庫表結構
CREATE TABLE USERINFO (
NAME VARCHAR2(50) NOT NULL,
LOGINNAME VARCHAR2(50) PRIMARY KEY,
LOGINPSW VARCHAR2(100) NOT NULL -- 存儲 BCrypt 雜湊值
);
3. DAO 實現(核心 JDBC 代碼)
package com.example.auth.dao;
import com.example.auth.entity.User;
import java.sql.*;
public class UserDaoImpl implements UserDao {
private static final String URL = "jdbc:oracle:thin:@localhost:1521:orcl";
private static final String USER = "scott";
private static final String PASS = "tiger";
static {
try { Class.forName("oracle.jdbc.driver.OracleDriver"); }
catch (ClassNotFoundException e) { throw new RuntimeException("驅動加載失敗", e); }
}
private Connection getConn() throws SQLException {
return DriverManager.getConnection(URL, USER, PASS);
}
/** 登錄驗證:返回 User 或 null */
@Override
public User findByLoginNameAndPassword(String loginName, String password) {
String sql = "SELECT NAME FROM USERINFO WHERE LOGINNAME = ? AND LOGINPSW = ?";
try (Connection conn = getConn();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, loginName);
ps.setString(2, password); // 實際應傳入加密後的密碼
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
User u = new User();
u.setLoginName(loginName);
u.setName(rs.getString("NAME"));
return u;
}
}
} catch (SQLException e) {
throw new RuntimeException("登錄查詢異常", e);
}
return null;
}
/** 註冊:檢查用戶名唯一性並插入 */
@Override
public boolean register(User user) {
// 1. 檢查登錄名是否存在
String checkSql = "SELECT 1 FROM USERINFO WHERE LOGINNAME = ?";
try (Connection conn = getConn();
PreparedStatement ps = conn.prepareStatement(checkSql)) {
ps.setString(1, user.getLoginName());
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return false; // 已存在
}
} catch (SQLException e) {
throw new RuntimeException("檢查用戶名異常", e);
}
// 2. 插入新用戶
String insertSql = "INSERT INTO USERINFO (NAME, LOGINNAME, LOGINPSW) VALUES (?, ?, ?)";
try (Connection conn = getConn();
PreparedStatement ps = conn.prepareStatement(insertSql)) {
ps.setString(1, user.getName());
ps.setString(2, user.getLoginName());
ps.setString(3, user.getPassword()); // 必須是加密後的密碼
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("註冊插入異常", e);
}
}
}
4. 服務層業務邏輯
package com.example.auth.service;
import com.example.auth.dao.UserDao;
import com.example.auth.dao.UserDaoImpl;
import com.example.auth.entity.User;
public class AuthServiceImpl implements AuthService {
private final UserDao userDao = new UserDaoImpl();
@Override
public User login(String loginName, String rawPassword) {
// 實際應對 rawPassword 進行 BCrypt 加密後再查詢
String encrypted = BCrypt.hashpw(rawPassword, BCrypt.gensalt()); // 示例
return userDao.findByLoginNameAndPassword(loginName, encrypted);
}
@Override
public boolean register(String name, String loginName, String rawPassword) {
if (name == null || loginName == null || rawPassword == null) return false;
User user = new User();
user.setName(name);
user.setLoginName(loginName);
user.setPassword(BCrypt.hashpw(rawPassword, BCrypt.gensalt())); // 加密存儲
return userDao.register(user);
}
}
5. 控制台測試入口
package com.example.auth;
import com.example.auth.entity.User;
import com.example.auth.service.AuthService;
import com.example.auth.service.AuthServiceImpl;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
AuthService auth = new AuthServiceImpl();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("\n=== 用戶系統 ===");
System.out.println("1. 登錄");
System.out.println("2. 註冊");
System.out.println("3. 退出");
System.out.print("請選擇: ");
int choice = sc.nextInt();
sc.nextLine(); // 消耗換行
switch (choice) {
case 1:
System.out.print("登錄名: "); String ln = sc.nextLine();
System.out.print("密碼: "); String pw = sc.nextLine();
User u = auth.login(ln, pw);
if (u != null) System.out.println("歡迎 " + u.getName() + " 登錄成功!");
else System.out.println("登錄失敗:賬號或密碼錯誤");
break;
case 2:
System.out.print("顯示名: "); String name = sc.nextLine();
System.out.print("登錄名: "); String regLn = sc.nextLine();
System.out.print("密碼: "); String regPw = sc.nextLine();
if (auth.register(name, regLn, regPw))
System.out.println("註冊成功!");
else
System.out.println("註冊失敗:登錄名已存在");
break;
case 3:
System.out.println("再見!");
return;
default:
System.out.println("無效選項");
}
}
}
}
關鍵最佳實踐總結
| 要點 | 做法 | 原因 |
| 防 SQL 注入 | 全程使用 PreparedStatement + 參數綁定 | 預編譯 SQL,參數不參與語法解析 |
| 密碼安全 | 存儲 BCrypt/SCrypt/Argon2 雜湊值,絕不存明文 | 防止數據洩露後被撞庫、彩虹表破解 |
| 資源管理 | try-with-resources 自動關閉 Connection/Statement/ResultSet | 避免連接洩漏導致連接池耗盡 |
| 異常處理 | 捕獲 SQLException 轉為運行時異常上拋,由上層統一處理 | 保持代碼整潔,避免業務邏輯被 try-catch 污染 |
| 連接池 | 生產環境必須使用 HikariCP / Druid 等連接池 | 頻繁創建銷毀連接性能極差 |
總結
本文提供了從實體、DAO、Service 到主程序的完整分層架構,採用 PreparedStatement 防注入、BCrypt 加密密碼、try-with-resources 管理資源,是 Java JDBC 連接 Oracle 實現用戶註冊登錄的生產級參考模板。
- 上一篇文章: Java 排序算法深度剖析:JDK 7 Dual-Pivot QuickSort 核心源碼解讀與優化思路
- 下一篇文章: Java RandomAccessFile 實現文件任意位置插入內容完整代碼詳解
Copyright ©
程式碼之家 All Rights Reserved