首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP + MySQL 實作用戶註冊登入系統:含驗證碼、密碼加密、Session 管理

     編輯:PHP 綜合知識

本文提供一套完整的 PHP + MySQL 用戶註冊登入範例,涵蓋資料庫建表、註冊驗證 (含圖形驗證碼)、密碼雜湊加密、Session 登入態管理、登出功能,程式碼結構清晰,適合初學者參考與二次開發。

資料庫建表 SQL

CREATE DATABASE IF NOT EXISTS `user_demo` DEFAULT CHARSET utf8mb4;
USE `user_demo`;

CREATE TABLE `users` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用戶名',
  `email` VARCHAR(100) NOT NULL UNIQUE COMMENT '電子郵件',
  `password` VARCHAR(255) NOT NULL COMMENT '密碼雜湊',
  `salt` VARCHAR(32) NOT NULL COMMENT '加密鹽值',
  `reg_ip` VARCHAR(45) DEFAULT NULL COMMENT '註冊 IP',
  `reg_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '註冊時間',
  `last_login_time` INT UNSIGNED DEFAULT NULL COMMENT '最後登入時間',
  `last_login_ip` VARCHAR(45) DEFAULT NULL COMMENT '最後登入 IP',
  `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1=正常 0=禁用'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

配置檔 config.php

<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'user_demo');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_CHARSET', 'utf8mb4');

define('SITE_KEY', 'your_random_secret_key_here'); // 用於密碼加鹽、Token 簽名

session_start();

// 資料庫連線 (PDO)
try {
    $pdo = new PDO(
        "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
        DB_USER, DB_PASS,
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );
} catch (PDOException $e) {
    die('資料庫連線失敗: ' . $e->getMessage());
}
?>

驗證碼產生 captcha.php

<?php
require 'config.php';
session_start();

$width = 100; $height = 36;
$image = imagecreatetruecolor($width, $height);
$bg = imagecolorallocate($image, 240, 240, 240);
imagefill($image, 0, 0, $bg);

// 干擾線
for ($i=0; $i<5; $i++) {
    $color = imagecolorallocate($image, rand(150,200), rand(150,200), rand(150,200));
    imageline($image, 0, rand(0,$height), $width, rand(0,$height), $color);
}

// 干擾點
for ($i=0; $i<50; $i++) {
    $color = imagecolorallocate($image, rand(100,200), rand(100,200), rand(100,200));
    imagesetpixel($image, rand(0,$width), rand(0,$height), $color);
}

// 產生 4 位隨機字元
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = '';
for ($i=0; $i<4; $i++) $code .= $chars[rand(0, strlen($chars)-1)];
$_SESSION['captcha'] = strtoupper($code);

// 繪製文字
$font_size = 18;
$colors = [
    imagecolorallocate($image, 0, 0, 0),
    imagecolorallocate($image, 100, 0, 0),
    imagecolorallocate($image, 0, 100, 0),
];
for ($i=0; $i<4; $i++) {
    $x = 15 + $i * 20;
    $y = rand(24, 28);
    $color = $colors[array_rand($colors)];
    imagestring($image, 5, $x, $y, $code[$i], $color);
}

header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
?>

註冊處理 register.php

<?php
require 'config.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $email    = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';
    $captcha  = strtoupper(trim($_POST['captcha'] ?? ''));

    // 基礎驗證
    $errors = [];
    if (!$username) $errors[] = '用戶名不可為空';
    if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = '信箱格式錯誤';
    if (strlen($password) < 6) $errors[] = '密碼至少 6 碼';
    if ($captcha !== ($_SESSION['captcha'] ?? '')) $errors[] = '驗證碼錯誤';

    // 檢查重複
    if (empty($errors)) {
        $stmt = $pdo->prepare("SELECT id FROM users WHERE username=? OR email=?");
        $stmt->execute([$username, $email]);
        if ($stmt->fetch()) $errors[] = '用戶名或信箱已存在';
    }

    // 寫入資料庫
    if (empty($errors)) {
        $salt = bin2hex(random_bytes(16));
        $hash = password_hash($password . $salt, PASSWORD_DEFAULT);
        $ip = $_SERVER['REMOTE_ADDR'];
        $time = time();

        $stmt = $pdo->prepare(
            "INSERT INTO users (username, email, password, salt, reg_ip, reg_time) VALUES (?,?,?,?,?,?)"
        );
        $stmt->execute([$username, $email, $hash, $salt, $ip, $time]);

        header('Location: login.php?reg=ok');
        exit;
    }

    // 顯示錯誤 (簡易示範)
    echo '
  • ' . implode('
  • ', $errors) . '
'; } ?> <!DOCTYPE html> <html><head><meta charset="utf-8"><title>用戶註冊</title></head><body> <h2>用戶註冊</h2> <form method="post"> 用戶名:<input name="username" required><br> 信箱:<input name="email" type="email" required><br> 密碼:<input name="password" type="password" required><br> 驗證碼:<input name="captcha" required> <img src="captcha.php" id="captcha-img" style="vertical-align:middle;cursor:pointer" title="點擊換圖"> <script>document.getElementById('captcha-img').onclick=function(){this.src='captcha.php?'+Date.now();}</script> <br> <button type="submit">註冊</button> </form> <p><a href="login.php">已有帳號?去登入</a></p> </body></html>

登入處理 login.php

<?php
require 'config.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';

    $stmt = $pdo->prepare("SELECT id, username, password, salt, status FROM users WHERE username=? OR email=?");
    $stmt->execute([$username, $username]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($user && $user['status'] == 1 && password_verify($password . $user['salt'], $user['password'])) {
        // 登入成功
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];

        // 更新最後登入資訊
        $stmt = $pdo->prepare("UPDATE users SET last_login_time=?, last_login_ip=? WHERE id=?");
        $stmt->execute([time(), $_SERVER['REMOTE_ADDR'], $user['id']]);

        header('Location: member.php');
        exit;
    } else {
        $error = '帳號不存在、密碼錯誤或帳號已停用';
    }
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>用戶登入</title></head><body>
<h2>用戶登入</h2>
<?php if (!empty($error)) echo '

'.$error.'

'; ?> <form method="post"> 帳號:<input name="username" required autofocus><br> 密碼:<input name="password" type="password" required><br> <button type="submit">登入</button> </form> <p><a href="register.php">註冊新帳號</a></p> </body></html>

會員中心 member.php / 登出 logout.php

<?php
// member.php
require 'config.php';
if (empty($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
echo '

會員中心

'; echo '歡迎,' . htmlspecialchars($_SESSION['username']) . ' (ID: ' . $_SESSION['user_id'] . ')'; echo '
登出'; ?> <?php // logout.php require 'config.php'; session_destroy(); header('Location: login.php'); ?>

安全性關鍵點總結

項目做法風險若不做
密碼加密password_hash() + 隨機鹽值資料庫洩露導致明文密碼外洩
SQL 注入防護PDO 預處理語句 (prepare/execute)任意 SQL 執行、資料竄改/刪除
XSS 防護輸出時 htmlspecialchars()惡意腳本竊取 Cookie、劫持會話
CSRF 防護表單加入 Token 驗證偽造請求修改密碼/轉帳等敏感操作
驗證碼圖形驗證碼 + Session 儲存暴力破解、自動化註冊機器人
Session 安全session.cookie_httponly=1, session.use_strict_mode=1Session 劫持、固定攻擊

擴充建議

  • 加入 Email 驗證流程 (發送驗證連結、Token 驗證)
  • 實作「記住我」功能 (長效 Token + 自動登入)
  • 密碼重設流程 (郵件重設連結、時效性 Token)
  • 引入 Composer 管理依賴,採用 PSR-4 自動載入、MVC 分層
  • 使用 vlucas/phpdotenv 管理環境變數,配置與程式碼分離

此範例展示了現代 PHP 開發的核心安全實踐,建議在實際專案中進一步模組化、加入單元測試與 CI/CD 流程。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved