NTM Solutions

Chủ Nhật, 2 tháng 8, 2026

🚀 Laravel 12 (2026) — Bài 24 — Middleware

Ở các bài trước, chúng ta đã cài đặt Laravel Breeze, đăng ký tài khoản và đăng nhập vào hệ thống.

Tuy nhiên, hiện tại nếu người dùng biết đường dẫn trực tiếp như:

/posts/create

hoặc:

/dashboard

thì liệu họ có thể truy cập được hay không?

Đó chính là nhiệm vụ của Middleware.

Middleware đóng vai trò như một lớp bảo vệ, kiểm tra mọi yêu cầu trước khi cho phép truy cập vào Controller hoặc Route.


Middleware là gì?

Middleware là đoạn mã nằm giữa RequestController.

Mọi yêu cầu gửi đến Laravel đều có thể đi qua một hoặc nhiều Middleware.

Sơ đồ:

Người dùng

      │

      ▼

HTTP Request

      │

      ▼

Middleware

      │

      ▼

Controller

      │

      ▼

Response

Nếu Middleware từ chối yêu cầu, Controller sẽ không được thực thi.


Ví dụ thực tế

Giả sử Blog có các chức năng:

  • Xem bài viết.

  • Thêm bài viết.

  • Chỉnh sửa bài viết.

  • Xóa bài viết.

Khách chưa đăng nhập:

✅ Được xem bài viết.

❌ Không được tạo bài viết.

❌ Không được sửa bài viết.

❌ Không được xóa bài viết.

Middleware sẽ đảm nhận việc kiểm tra này.


Middleware auth

Laravel đã có sẵn Middleware:

auth

Nó sẽ kiểm tra:

Người dùng đã đăng nhập?

        │

   Có         Không

    │            │

    ▼            ▼

 Cho phép    Chuyển Login

Bảo vệ Route

Ví dụ:

use App\Http\Controllers\PostController;

Route::middleware('auth')->group(function () {

    Route::resource(
        'posts',
        PostController::class
    );

});

Toàn bộ các Route của PostController đều yêu cầu đăng nhập.


Chỉ bảo vệ một Route

Ví dụ:

Route::get(

    '/dashboard',

    function () {

        return view('dashboard');

    }

)->middleware('auth');

Nếu chưa đăng nhập, Laravel sẽ chuyển người dùng đến trang Login.


Kiểm tra

Đăng xuất khỏi hệ thống.

Sau đó truy cập:

/posts/create

Kết quả:

302 Redirect

↓

/login

Đây là hành vi mặc định của Middleware auth.


Kết hợp với Breeze

Sau khi cài Laravel Breeze, Route dashboard thường được bảo vệ như sau:

Route::get('/dashboard', function () {

    return view('dashboard');

})->middleware(['auth']);

Điều này đảm bảo chỉ người dùng đã xác thực mới truy cập được.


Middleware guest

Laravel cũng có Middleware:

guest

Ý nghĩa:

  • Chưa đăng nhập → được truy cập.

  • Đã đăng nhập → chuyển hướng.

Ví dụ:

Route::middleware('guest')->group(function () {

    Route::get(
        '/login',
        ...
    );

    Route::get(
        '/register',
        ...
    );

});

Người đã đăng nhập( k phải guest) sẽ không thể mở lại trang Login hoặc Register.


Middleware nhiều lớp

Một Route có thể sử dụng nhiều Middleware.

Ví dụ:

Route::middleware([

    'auth',

    'verified',

])->group(function () {

    //

});

Laravel sẽ kiểm tra lần lượt:

  • Đã đăng nhập.

  • Email đã xác minh.

Nếu một bước không đạt, yêu cầu sẽ bị chặn.


Tạo Middleware mới

Tạo bằng Artisan:

php artisan make:middleware CheckPostOwner

Laravel sẽ tạo:

app/

└── Http/

    └── Middleware/

        CheckPostOwner.php

Ví dụ Middleware

public function handle(
    Request $request,
    Closure $next
)
{
    if (!auth()->check()) {

        return redirect()
            ->route('login');

    }

    return $next($request);
}

$next($request) nghĩa là cho phép Request tiếp tục đi tới Controller.


Chỉ cho tác giả sửa bài

Giả sử mỗi Post có:

user_id

Middleware:

$post = $request->route('post');

if (

    $post->user_id != auth()->id()

) {

    abort(403);

}

return $next($request);

Nếu người dùng không phải là tác giả:

403 Forbidden

Đây là cách bảo vệ dữ liệu rất phổ biến trong Blog CMS.


Áp dụng cho Profile

Chú ý: ví dụ này chỉ minh họa vì Breeze thiết kế để mỗi user chỉ nhìn thấy profile của chính mình nên k cần middleware.

trước khi chạy link /profile nên dùng lệnh tạo CategoryController trước (do đã khai báo trong route nhưng chưa tạo dẫn đến lỗi trong route:list)

/profile/5/edit

Middleware kiểm tra:

$profile = $request
    ->route('profile');

if (

    $profile->user_id != auth()->id()

) {

    abort(403);

}

Người dùng chỉ được sửa Profile của chính mình.


Middleware trong thực tế

Một số Middleware thường gặp:

MiddlewareChức năng
authKiểm tra đăng nhập
guestChỉ cho khách truy cập
verifiedEmail đã xác minh
throttleGiới hạn số lần gửi Request
signedKiểm tra URL có chữ ký
customMiddleware tự tạo

Middleware và Request

Quy trình xử lý:

Request

      │

      ▼

Middleware

      │

      ▼

Validation

      │

      ▼

Controller

      │

      ▼

Model

      │

      ▼

Database

Middleware luôn hoạt động trước Controller.


Tổng kết

Trong bài này chúng ta đã học:

  • Middleware là gì.

  • Middleware auth.

  • Middleware guest.

  • Bảo vệ Route.

  • Route Group.

  • Tạo Middleware bằng Artisan.

  • $next($request).

  • abort(403).

  • Kiểm tra chủ sở hữu của Post và Profile.

Middleware là tuyến phòng thủ đầu tiên của ứng dụng Laravel, giúp bảo vệ các chức năng quan trọng trước khi dữ liệu được xử lý.


Bài tập

  1. Bảo vệ toàn bộ Route posts bằng Middleware auth.

  2. Kiểm tra truy cập /dashboard khi chưa đăng nhập.

  3. Tạo Middleware CheckPostOwner.

  4. Chỉ cho phép tác giả sửa hoặc xóa bài viết của mình.

  5. Áp dụng Middleware cho chức năng chỉnh sửa Profile.

  6. Thử truy cập bằng tài khoản khác và quan sát kết quả 403 Forbidden.

  7. Kết hợp authverified cho một Route bất kỳ.


Bài tiếp theo

Bài 25, chúng ta sẽ tìm hiểu Authorization, cơ chế phân quyền trong Laravel bằng GatePolicy, giúp xác định ai được phép thực hiện hành động nào trên từng đối tượng dữ liệu như Post, User hoặc Category.


BONUS

Phần view posts (hiện tại mới xây dựng có search theo category)

Có thể dùng CDN.

<link rel="stylesheet"
href="https://cdn.datatables.net/2.3.4/css/dataTables.dataTables.min.css">

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<script src="https://cdn.datatables.net/2.3.4/js/dataTables.min.js"></script>

1. Table

<table id="postsTable" class="min-w-full">

<thead>

<tr>

    <th>ID</th>
    <th>Tiêu đề</th>
    <th>User</th>
    <th>Ngày đăng</th>

</tr>

</thead>

<tbody>

@foreach($posts as $post)

<tr>

    <td>{{ $post->id }}</td>

    <td>{{ $post->title }}</td>

    <td>{{ $post->user->name }}</td>

    <td>{{ $post->created_at->format('d/m/Y') }}</td>

</tr>

@endforeach

</tbody>

</table>

2. Khởi tạo

new DataTable('#postsTable', {

    pageLength: 10,

    responsive: true,

    ordering: true,

    searching: true,

    info: true,

    lengthChange: true

});

Là có ngay

  • Search
  • Sort
  • Pagination

3. Việt hóa

new DataTable('#postsTable', {

    language: {

        search: "🔍 Tìm kiếm:",

        lengthMenu: "Hiển thị _MENU_ dòng",

        info: "Hiển thị _START_ đến _END_ / _TOTAL_",

        paginate: {
            first: "Đầu",
            last: "Cuối",
            next: "Sau",
            previous: "Trước"
        },

        zeroRecords: "Không tìm thấy dữ liệu",

        emptyTable: "Chưa có dữ liệu"

    }

});

4. Lúc này Controller chỉ cần

public function index()
{
    $posts = Post::with('user')
                ->latest()
                ->get();

    return view('posts.index', compact('posts'));
}

Không cần code search nữa vì DataTables search ngay trên trình duyệt.


5. Sau này nâng cấp

Khi số lượng bài viết lớn (ví dụ 10.000–100.000 bài), không nên tải tất cả về trình duyệt. Khi đó nên dùng Server-side Processing của DataTables:

return DataTables::eloquent(
    Post::with('user')
)->toJson();

Kết hợp với gói Yajra DataTables trong Laravel để:

  • Search ở database.
  • Sort ở database.
  • Pagination ở database.
  • Tốc độ rất nhanh dù có hàng trăm nghìn bản ghi.

Mục 4 nên hiển thị all posts cho admin only...

Trong Laravel nên xử lý ở Controller bằng cách kiểm tra role/admin trước khi query.

Ví dụ bạn đang có cột role trong bảng users (admin, editor, user) như các bài trước, có thể làm:

use Illuminate\Support\Facades\Auth;

public function index()
{
    $user = Auth::user();

    if ($user->isAdmin()) {
        // Admin xem tất cả bài viết
        $posts = Post::with('user')
                    ->latest()
                    ->get();
    } else {
        // User thường chỉ xem bài của mình
        $posts = Post::with('user')
                    ->where('user_id', $user->id)
                    ->latest()
                    ->get();
    }

    return view('posts.index', compact('posts'));
}

Sau đó thêm function isAdmin() trong User Model:

app/Models/User.php

public function isAdmin()
{
    return strtolower($this->role) === 'admin';
}

Khi đó logic sẽ là:

UserQuery
AdminPost::all() → thấy tất cả posts
Userwhere('user_id', Auth::id()) → chỉ thấy posts của mình

Có thể viết gọn hơn bằng toán tử 3 ngôi:

public function index()
{
    $user = Auth::user();

    $posts = Post::with('user')
        ->when(!$user->isAdmin(), function ($query) use ($user) {
            $query->where('user_id', $user->id);
        })
        ->latest()
        ->get();

    return view('posts.index', compact('posts'));
}

Cách này Laravel style hơn vì chỉ có một query chain.

Sau này mở rộng thêm:

  • Admin → CRUD tất cả

  • Editor → sửa bài được giao

  • User → chỉ CRUD bài của mình

thì nên chuyển sang Laravel Policy + Gate thay vì kiểm tra trực tiếp trong Controller.

x1

quay về MỤC LỤC

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS