Tiếp Phần 5 (đã có giỏ hàng). Hôm nay làm hệ thống thành viên: đăng ký, đăng nhập, đăng xuất. Bảng users đã tạo sẵn từ Phần 2 rồi nên phần này chủ yếu là xử lý form + mật khẩu. ============================== 1. FILE register.php (đăng ký) ============================== <?php require_once __DIR__ . '/includes/functions.php'; if (is_logged_in()) { redirect('account.php'); } $page_title = 'Dang ky tai khoan'; $errors = []; if ($_SERVER['REQUEST_METHOD'] === 'POST') { csrf_verify(); $full_name = trim($_POST['full_name'] ?? ''); $email = trim($_POST['email'] ?? ''); $phone = trim($_POST['phone'] ?? ''); $password = $_POST['password'] ?? ''; $password_confirm = $_POST['password_confirm'] ?? ''; if ($full_name === '' || mb_strlen($full_name) < 2) { $errors[] = 'Vui long nhap ho ten hop le.'; } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = 'Email khong hop le.'; } if (mb_strlen($password) < 6) { $errors[] = 'Mat khau phai co it nhat 6 ky tu.'; } if ($password !== $password_confirm) { $errors[] = 'Xac nhan mat khau khong khop.'; } if (empty($errors)) { $stmt = $pdo->prepare('SELECT id FROM users WHERE email = ?'); $stmt->execute([$email]); if ($stmt->fetch()) { $errors[] = 'Email nay da duoc dang ky.'; } } if (empty($errors)) { $hash = password_hash($password, PASSWORD_DEFAULT); $stmt = $pdo->prepare( 'INSERT INTO users (full_name, email, phone, password_hash, role) VALUES (?, ?, ?, ?, "customer")' ); $stmt->execute([$full_name, $email, $phone, $hash]); $_SESSION['user_id'] = (int) $pdo->lastInsertId(); $_SESSION['user_name'] = $full_name; $_SESSION['user_role'] = 'customer'; session_regenerate_id(true); redirect('account.php'); } } require __DIR__ . '/includes/header.php'; ?> <div class="form-box"> <h1 class="page-title">Dang ky tai khoan</h1> <?php foreach ($errors as $err): ?> <div class="alert alert-error"><?= e($err) ?></div> <?php endforeach; ?> <form action="register.php" method="post"> <?= csrf_field() ?> <div class="form-group"> <label>Ho ten</label> <input type="text" name="full_name" value="<?= e($_POST['full_name'] ?? '') ?>" required> </div> <div class="form-group"> <label>Email</label> <input type="email" name="email" value="<?= e($_POST['email'] ?? '') ?>" required> </div> <div class="form-group"> <label>So dien thoai</label> <input type="text" name="phone" value="<?= e($_POST['phone'] ?? '') ?>"> </div> <div class="form-group"> <label>Mat khau</label> <input type="password" name="password" required> </div> <div class="form-group"> <label>Xac nhan mat khau</label> <input type="password" name="password_confirm" required> </div> <button type="submit" class="btn" style="width:100%;">Dang ky</button> </form> <p style="margin-top:14px; text-align:center;">Da co tai khoan? <a href="login.php">Dang nhap</a></p> </div> <?php require __DIR__ . '/includes/footer.php'; ?> ============================== 2. FILE login.php (đăng nhập) ============================== <?php require_once __DIR__ . '/includes/functions.php'; if (is_logged_in()) { redirect('account.php'); } $page_title = 'Dang nhap'; $error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { csrf_verify(); $email = trim($_POST['email'] ?? ''); $password = $_POST['password'] ?? ''; $stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1'); $stmt->execute([$email]); $user = $stmt->fetch(); if ($user && password_verify($password, $user['password_hash'])) { $_SESSION['user_id'] = (int) $user['id']; $_SESSION['user_name'] = $user['full_name']; $_SESSION['user_role'] = $user['role']; session_regenerate_id(true); redirect($user['role'] === 'admin' ? 'admin/index.php' : 'account.php'); } $error = 'Email hoac mat khau khong dung.'; } require __DIR__ . '/includes/header.php'; ?> <div class="form-box"> <h1 class="page-title">Dang nhap</h1> <?php if ($error): ?> <div class="alert alert-error"><?= e($error) ?></div> <?php endif; ?> <form action="login.php" method="post"> <?= csrf_field() ?> <div class="form-group"> <label>Email</label> <input type="email" name="email" value="<?= e($_POST['email'] ?? '') ?>" required> </div> <div class="form-group"> <label>Mat khau</label> <input type="password" name="password" required> </div> <button type="submit" class="btn" style="width:100%;">Dang nhap</button> </form> <p style="margin-top:14px; text-align:center;">Chua co tai khoan? <a href="register.php">Dang ky ngay</a></p> </div> <?php require __DIR__ . '/includes/footer.php'; ?> ============================== 3. FILE logout.php (đăng xuất) ============================== <?php require_once __DIR__ . '/includes/functions.php'; $_SESSION = []; session_destroy(); redirect('index.php'); ============================== 4. FILE account.php (trang tài khoản - khung sườn, Phần 7 sẽ bổ sung lịch sử đơn hàng) ============================== <?php require_once __DIR__ . '/includes/functions.php'; require_login(); $page_title = 'Tai khoan cua toi'; $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$_SESSION['user_id']]); $user = $stmt->fetch(); require __DIR__ . '/includes/header.php'; ?> <h1 class="page-title">Xin chao, <?= e($user['full_name']) ?></h1> <p>Email: <?= e($user['email']) ?></p> <p>So dien thoai: <?= e($user['phone'] ?: 'Chua cap nhat') ?></p> <h2 style="margin-top:24px; font-size:18px;">Lich su don hang</h2> <p><em>(Se hoan thien o Phan 7 sau khi lam xong chuc nang dat hang)</em></p> <?php require __DIR__ . '/includes/footer.php'; ?> ============================== GIẢI THÍCH BẢO MẬT QUAN TRỌNG ============================== - Mật khẩu KHÔNG BAO GIỜ lưu dạng thường (plain text), luôn dùng password_hash() khi lưu và password_verify() khi kiểm tra - đây là chuẩn bắt buộc, tuyệt đối không tự viết hàm mã hoá riêng (md5, sha1 đều KHÔNG an toàn cho mật khẩu). - session_regenerate_id(true) được gọi ngay sau khi đăng nhập/đăng ký thành công: đổi session ID mới, chống tấn công "session fixation" (kẻ gian gài sẵn 1 session ID rồi dụ nạn nhân đăng nhập vào session đó). - logout.php xoá sạch $_SESSION rồi mới session_destroy() - xoá cả dữ liệu lẫn session trên server, không chỉ xoá cookie. - Route phân quyền: sau khi đăng nhập, nếu role là admin thì chuyển thẳng vào admin/index.php, còn customer thì vào account.php - khu vực admin sẽ được chốt chặn kỹ hơn ở Phần 8 bằng require_admin(). Phần 7 tiếp theo là phần quan trọng nhất: xử lý đặt hàng thật sự (tạo đơn hàng, trừ kho, xoá giỏ hàng) và hiển thị lịch sử đơn hàng cho khách. Hẹn gặp lại! --- Hết Phần 6 --- ---------------------------------------- MỤC LỤC LOẠT BÀI: 1. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 1: Tổng Quan Dự Án, Yêu Cầu Môi Trường & Cấu Trúc Thư Mục - https://diendan.anyvlogs.info/chu-de/457-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-1-tong-quan-du-an-yeu-cau-moi-truong-cau-truc-thu-muc 2. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 2: Thiết Kế Cơ Sở Dữ Liệu (Database) Đầy Đủ Cho Website Bán Hàng - https://diendan.anyvlogs.info/chu-de/458-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-2-thiet-ke-co-so-du-lieu-database-day-du-cho-website-ban-hang 3. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 3: Dựng Khung Ứng Dụng - Kết Nối PDO, Hàm Dùng Chung, Giao Diện Chung - https://diendan.anyvlogs.info/chu-de/459-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-3-dung-khung-ung-dung-ket-noi-pdo-ham-dung-chung-giao-dien-chung 4. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 4: Trang Chủ, Danh Mục Sản Phẩm & Chi Tiết Sản Phẩm - https://diendan.anyvlogs.info/chu-de/460-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-4-trang-chu-danh-muc-san-pham-chi-tiet-san-pham 5. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 5: Xây Dựng Giỏ Hàng (Cart) Bằng Session - https://diendan.anyvlogs.info/chu-de/461-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-5-xay-dung-gio-hang-cart-bang-session >> 6. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 6: Đăng Ký, Đăng Nhập, Đăng Xuất Thành Viên - https://diendan.anyvlogs.info/chu-de/462-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-6-dang-ky-dang-nhap-dang-xuat-thanh-vien 7. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 7: Đặt Hàng, Thanh Toán COD & Lịch Sử Đơn Hàng - https://diendan.anyvlogs.info/chu-de/463-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-7-dat-hang-thanh-toan-cod-lich-su-don-hang 8. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 8: Trang Quản Trị (Admin) - Quản Lý Danh Mục, Sản Phẩm, Đơn Hàng - https://diendan.anyvlogs.info/chu-de/464-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-8-trang-quan-tri-admin-quan-ly-danh-muc-san-pham-don-hang 9. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 9: Bảo Mật Website (CSRF, XSS, SQL Injection, Upload An Toàn) - https://diendan.anyvlogs.info/chu-de/465-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-9-bao-mat-website-csrf-xss-sql-injection-upload-an-toan 10. [Series PHP+MySQL] Làm Website Bán Hàng Từ A-Z - Phần 10 (Kết Thúc): Tối Ưu Hiệu Năng & Triển Khai Lên Hosting Thật - https://diendan.anyvlogs.info/chu-de/466-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-10-ket-thuc-toi-uu-hieu-nang-trien-khai-len-hosting-that Phần trước: https://diendan.anyvlogs.info/chu-de/461-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-5-xay-dung-gio-hang-cart-bang-session Phần sau: https://diendan.anyvlogs.info/chu-de/463-series-php-mysql-lam-website-ban-hang-tu-a-z-phan-7-dat-hang-thanh-toan-cod-lich-su-don-hang