0

PHP và MySQL Bài 27: Xây dựng hệ thống Đăng ký, Đăng nhập và Phân quyền cơ bản (Mô hình MVC)

Trong hệ thống thực tế, việc tách biệt trách nhiệm xử lý dữ liệu (Model) và luồng điều khiển (Controller) là bước đệm bắt buộc để tiến tới các thiết kế cấp cao như Clean Architecture. Hệ thống Xác thực (Authentication) và Phân quyền (Authorization) là tính năng hoàn hảo nhất để thực hành tư duy này [cite: x].

Chúng ta sẽ bám sát cấu trúc thư mục đã định nghĩa ở Bài 26 để viết code [cite: x].


1. Xây dựng lớp Model (models/User.php)

Nhiệm vụ duy nhất của Model User là "nói chuyện" với cơ sở dữ liệu [cite: x]. Nó không quan tâm đến việc người dùng nhập gì từ Form, nó chỉ nhận tham số và trả về kết quả truy vấn SQL [cite: x].

Tạo file models/User.php:

<?php
class User {
    private $pdo;

    // Khởi tạo Model với kết nối Database
    public function __construct($pdoConnection) {
        $this->pdo = $pdoConnection;
    }

    // Hàm thêm người dùng mới (Dùng cho Đăng ký)
    public function createUser($fullname, $email, $hashedPassword) {
        $sql = "INSERT INTO users (fullname, email, password, role) VALUES (?, ?, ?, 0)";
        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute([$fullname, $email, $hashedPassword]);
    }

    // Hàm lấy thông tin người dùng bằng Email (Dùng cho Đăng nhập)
    public function getUserByEmail($email) {
        $sql = "SELECT * FROM users WHERE email = ?";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$email]);
        return $stmt->fetch();
    }
}

2. Xây dựng lớp Controller (controllers/AuthController.php)

Controller đóng vai trò là nhạc trưởng [cite: x]. Nó nhận yêu cầu từ trình duyệt, lấy dữ liệu POST, gọi Model User để xử lý, và quyết định lưu Session hay báo lỗi [cite: x].

Tạo file controllers/AuthController.php:

<?php
require_once 'models/User.php';

class AuthController {
    private $userModel;

    public function __construct($pdo) {
        $this->userModel = new User($pdo);
    }

    // Xử lý luồng Đăng ký
    public function register() {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $fullname = trim($_POST['fullname']);
            $email = trim($_POST['email']);
            $password = $_POST['password'];

            // Validate cơ bản
            if (empty($fullname) || empty($email) || empty($password)) {
                die("Vui lòng điền đầy đủ thông tin.");
            }

            // Băm mật khẩu bằng thuật toán Bcrypt chuẩn
            $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

            // Gọi Model để lưu vào Database
            if ($this->userModel->createUser($fullname, $email, $hashedPassword)) {
                echo "Đăng ký thành công! Hãy chuyển sang trang Đăng nhập.";
            } else {
                echo "Email này đã được sử dụng.";
            }
        } else {
            // Nếu là GET request, hiển thị Form đăng ký
            require 'views/client/register.php';
        }
    }

    // Xử lý luồng Đăng nhập
    public function login() {
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $email = trim($_POST['email']);
            $password = $_POST['password'];

            // Lấy dữ liệu người dùng từ Database qua Model
            $user = $this->userModel->getUserByEmail($email);

            // Kiểm tra User tồn tại và verify mật khẩu
            if ($user && password_verify($password, $user['password'])) {
                session_start();
                
                // Tái tạo Session ID để chống Session Hijacking
                session_regenerate_id(true);

                // Lưu thông tin vào phiên làm việc
                $_SESSION['is_logged_in'] = true;
                $_SESSION['user_id'] = $user['id'];
                $_SESSION['role'] = $user['role'];
                $_SESSION['fullname'] = $user['fullname'];

                // Điều hướng dựa trên quyền (Authorization)
                if ($user['role'] == 1) {
                    header("Location: index.php?action=admin_dashboard");
                } else {
                    header("Location: index.php?action=home");
                }
                exit();
            } else {
                echo "Sai email hoặc mật khẩu!";
            }
        } else {
            // Nếu là GET request, hiển thị Form đăng nhập
            require 'views/client/login.php';
        }
    }
}

3. Tạo Giao diện View (views/client/login.php)

View chỉ thuần túy là HTML [cite: x]. Dưới đây là một form đăng nhập đơn giản. Form đăng ký sẽ có cấu trúc tương tự [cite: x].

<!DOCTYPE html>
<html>
<head>
    <title>Đăng nhập hệ thống</title>
</head>
<body>
    <h2>Đăng Nhập</h2>
    <!-- Thuộc tính action trỏ về bộ định tuyến trung tâm -->
    <form method="POST" action="index.php?action=login">
        <label>Email:</label>
        <input type="email" name="email" required><br><br>
        
        <label>Mật khẩu:</label>
        <input type="password" name="password" required><br><br>
        
        <button type="submit">Đăng nhập</button>
    </form>
</body>
</html>

4. Thiết lập Front Controller (index.php)

Như đã thống nhất ở Bài 26, file index.php ở thư mục gốc sẽ là cánh cửa duy nhất tiếp nhận mọi yêu cầu [cite: x]. Nó đóng vai trò là một "Bộ định tuyến" (Router) đơn giản để gọi đúng Controller cần thiết [cite: x].

Tạo file index.php ở thư mục gốc:

<?php
// Mở session cho toàn bộ hệ thống
session_start();

// Gọi file kết nối CSDL
require_once 'config/database.php';
require_once 'controllers/AuthController.php';

// Khởi tạo Controller
$authController = new AuthController($pdo);

// Lấy tham số action trên URL (Mặc định là 'home' nếu không truyền)
$action = isset($_GET['action']) ? $_GET['action'] : 'home';

// Bộ định tuyến (Routing)
switch ($action) {
    case 'login':
        $authController->login();
        break;
        
    case 'register':
        $authController->register();
        break;
        
    case 'admin_dashboard':
        // Cần kiểm tra quyền trước khi cho vào trang quản trị
        if (isset($_SESSION['is_logged_in']) && $_SESSION['role'] == 1) {
            echo "<h1>Chào mừng Admin " . $_SESSION['fullname'] . "</h1>";
            // require 'views/admin/dashboard.php';
        } else {
            echo "Bạn không có quyền truy cập khu vực này!";
        }
        break;
        
    case 'home':
    default:
        echo "<h1>Trang chủ Client</h1>";
        if (isset($_SESSION['is_logged_in'])) {
            echo "Xin chào, " . $_SESSION['fullname'];
        }
        break;
}

5. Phân quyền cơ bản (Authorization)

Hệ thống xác thực (Authentication - Biết người dùng là ai) đã hoàn tất [cite: x]. Bước tiếp theo là Phân quyền (Authorization - Biết người dùng được làm gì) [cite: x].

Trong đoạn code Router (index.php) ở trên, chúng ta đã áp dụng cơ chế phân quyền dựa trên Role (RBAC - Role Based Access Control) ở mức độ cơ bản nhất [cite: x]:

  • Kiểm tra isset($_SESSION['is_logged_in']): Xác nhận trạng thái đăng nhập [cite: x].
  • Kiểm tra $_SESSION['role'] == 1: Chỉ những tài khoản có cờ Admin mới được truy cập nhánh admin_dashboard [cite: x].

Trong các hệ thống thực tế đa người dùng, logic phân quyền này thường được tách riêng ra thành các lớp Middleware để tái sử dụng thay vì viết trực tiếp vào file Router [cite: x].


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í