NTM Solutions

Thứ Hai, 3 tháng 8, 2026

🚀 Laravel 12 (2026) — Bài 25 — Authorization trong Laravel 12

Kiểm soát quyền truy cập dữ liệu bằng Gate và Policy


Sau khi hoàn thành:

  • Bài 21 — Laravel Breeze

  • Bài 22 — Login

  • Bài 23 — Register

  • Bài 24 — Middleware

Chúng ta đã có hệ thống:

✅ Đăng ký tài khoản
✅ Đăng nhập
✅ Session Authentication
✅ Bảo vệ Route bằng Middleware

Nhưng hiện tại Laravel mới chỉ biết:

"Người này đã đăng nhập hay chưa?"

Ví dụ:

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

    Route::get('/dashboard', function(){
        return view('dashboard');
    });

});

Middleware auth chỉ kiểm tra:

User có đăng nhập?
        |
        |
       Có
        |
Cho phép truy cập

Nhưng trong thực tế CMS Blog cần nhiều hơn:

Ví dụ:

User A tạo bài viết:

ID: 10
Title: Laravel 12 Tutorial
User_id: 5

User B đăng nhập.

Nếu User B truy cập:

/posts/10/edit

thì sao?

Nếu chỉ dùng:

Route::middleware('auth')

Laravel sẽ cho phép.

Vấn đề:

User B
   |
   |
   V

Sửa bài viết của User A

Đây là lỗi phân quyền.


1. Authentication và Authorization khác nhau

Hai khái niệm rất dễ nhầm.


Authentication

(Authentication = Xác thực)

Câu hỏi:

Bạn là ai?

Ví dụ:

Login:

Email:
admin@gmail.com

Password:
******

Laravel kiểm tra:

Database users

email
password

Kết quả:

OK

User ID = 1

Authorization

(Authorization = Phân quyền)

Câu hỏi:

Bạn được phép làm gì?

Ví dụ:

User:

id: 5
name: Nam
role: user

Admin:

id: 1
name: Admin
role: admin

Quyền:

Chức năngAdminUser
Xem bài viết
Tạo bài viết
Sửa bài người khác
Xóa user
Quản lý hệ thống

2. Authorization trong Laravel

Laravel cung cấp 3 cơ chế chính:

                 Authorization
                       |
    ---------------------------------
    |               |              |
 Middleware       Gate         Policy
    |               |              |
 Chặn route     Logic nhỏ    Logic theo Model

3. Gate là gì?

Gate dùng cho các quyền đơn giản.

Ví dụ:

Chỉ Admin được vào trang quản trị.


Tạo Gate

Mở:

app/Providers/AppServiceProvider.php

Thêm:

use Illuminate\Support\Facades\Gate;
use App\Models\User;


public function boot(): void
{

    $this->configureDefaults();//dòng mặc định 13x
    
    Gate::define('admin', function(User $user){

        return $user->role === 'Admin';//có phân biệt Hoa-thường

    });

}

Ý nghĩa:

Khi gọi:

Gate::allows('admin')

Laravel chạy:

$user->role === 'admin'

Nếu:

admin

trả về:

true

4. Sử dụng Gate trong Controller

Ví dụ:

Tạo AdminController:

php artisan make:controller Admin/AdminController

Trong routes/web.php:

use App\Http\Controllers\Admin\AdminController;

Route::get('/admin', [AdminController::class, 'index']);

tạo trang views/admin/index.blade.php

<h1>Trang quản trị</h1>
<p>Chúc mừng! Bạn đã vượt qua Gate Authorization.</p>

AdminController:

use Illuminate\Support\Facades\Gate;


public function index()
{

    if(!Gate::allows('admin')){

        abort(403);

    }


    return view('admin.index');

}

Nếu User thường truy cập:

Chú ý: trong các bài trước nếu đã seeder ra 01 đống users thì vào UserFactory để biết mật khẩu.

/admin

Laravel trả:

403 Forbidden

5. Middleware Authorization

Laravel có sẵn middleware:

can

Ví dụ:

Route::get('/admin',
[
    AdminController::class,
    'index'

])
->middleware('can:admin');

Bây giờ:

Admin
 |
 OK


User
 |
 403

6. Policy là gì?

Gate phù hợp với quyền đơn giản.

Nhưng Blog CMS có nhiều quyền:

Ví dụ Post:

Post

create
read
update
delete
restore
forceDelete

Không nên viết:

if($user->role=='admin')

ở khắp Controller.

Code sẽ rất rối.

Giải pháp:

Policy

Policy gom toàn bộ quyền của một Model.

Ví dụ:

PostPolicy.php

7. Tạo Policy

Chạy:

php artisan make:policy PostPolicy --model=Post

Laravel tạo:

app
 |
 Policies
    |
    PostPolicy.php

Nội dung:

class PostPolicy
{

}

Laravel tạo sẵn:

viewAny()

view()

create()

update()

delete()

restore()

forceDelete()


8. Viết quyền Update Post

Ví dụ:

User chỉ sửa bài của mình.

Post:

id
title
content
user_id

User:

id = 5

Post:

user_id = 5

Cho phép.

Code:

public function update(
    User $user,
    Post $post
){

    return $user->id === $post->user_id;

}

Logic:

User đăng nhập

       |
       |

Post owner?

       |
 --------------
 |            |
Yes          No

Cho sửa       403

9. Đăng ký Policy

Laravel 12 tự động phát hiện theo convention:

Model:

App\Models\Post

Policy:

App\Policies\PostPolicy

Laravel tự map:

Post

+

PostPolicy

Không cần khai báo.


10. Dùng Policy trong Controller

Ví dụ:

PostController:

public function edit(Post $post)
{

    $this->authorize(
        'update',
        $post
    );


    return view(
        'posts.edit',
        compact('post')
    );

}

Nếu:

User_id = Post.user_id

cho phép.

Ngược lại:

403 Forbidden

11. Route Authorization

Có thể viết trực tiếp:

Route::get(
'/posts/{post}/edit',
[
PostController::class,
'edit'
]
)
->middleware(
'can:update,post'
);

Laravel hiểu:

can:

update

Model:

post

12. Blade kiểm tra quyền(---)

Trong giao diện:

Ví dụ nút Edit:

@can('update',$post)

<a href="#">
Edit
</a>

@endcan

Nếu User không có quyền:

Nút không xuất hiện.

Chú ý: bài trước mình lỡ cấu hình kỹ quá chỉ cho hiện posts theo user nên k thực hành được theo ví dụ này.

Giờ chuyển sang dấu nút Mở trang Admin trong link dashboard nếu k phải role admin

AppServiceProvider Laravel 12/13.

use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    $this->configureDefaults();

    Gate::define('admin', function (User $user) {
        return $user->role === 'Admin';
    });
}

Trong Blade:

@can('admin')
    <a href="{{ route('admin.index') }}"
       class="inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
        Mở
    </a>
@endcan

13. Thêm cột role cho User

Migration:

php artisan make:migration add_role_to_users_table

Migration:

Schema::table('users', function(Blueprint $table){

    $table->string('role')
          ->default('user');

});

Chạy:

php artisan migrate

Database:

users

idnamerole
1Adminadmin
2Namuser
3Lanuser

14. Tạo helper isAdmin()

Trong Model User:

public function isAdmin()
{

    return $this->role === 'admin';
    //nên chuyển sang ký tự thường cho an toàn 
    //return strtolower($this->role) === 'admin';

}

Sử dụng:

if(auth()->user()->isAdmin()){

    echo "Admin";

}

15. Ví dụ Blog CMS hoàn chỉnh

Admin

Dashboard

 |
 |
 CRUD Categories

 |
 |
 CRUD Users

 |
 |
 Delete Post

User

Dashboard

 |
 |
 Create Post

 |
 |
 Edit Post của mình

 |
 |
 Delete Post của mình

16. Luồng phân quyền chuẩn Laravel CMS

User Login

       |
       |

Authentication

       |
       |

Middleware auth

       |
       |

Authorization

       |
       |

Gate / Policy

       |
       |

Allow / Deny


17. Cấu trúc dự án sau bài 25

app

├── Models

│    ├── User.php
│    └── Post.php


├── Policies

│    └── PostPolicy.php


├── Http

│    ├── Controllers
│    │
│    └── Middleware


routes

└── web.php


18. Kiến thức đạt được sau bài này

Sau bài 25, người học hiểu:

✅ Authentication khác Authorization
✅ Gate trong Laravel
✅ Policy trong Laravel
$this->authorize()
can Middleware
✅ Blade @can
✅ Phân quyền Admin/User
✅ Bảo vệ dữ liệu theo User sở hữu


Chuẩn bị cho Bài 26

📘 Bài 26 — Roles & Permissions

Chúng ta sẽ nâng cấp từ:

role = admin/user

thành hệ thống thực tế:

Users

   |

Roles

   |

Permissions


Admin
 ├── create_post
 ├── edit_post
 ├── delete_user


Editor
 ├── edit_post


User
 ├── create_post

Kết hợp:

  • Middleware Admin

  • Permission table

  • Role table

  • Pivot relationship

  • Phân quyền CRUD hoàn chỉnh cho Blog CMS Laravel 12.


Errors log:

2 lỗi độc lập:

Lỗi 1: npm run build thất bại ✅ (nguyên nhân gốc)

Bạn đang dùng:

  • ✅ Tailwind 4.3.3
  • postcss.config.js của Tailwind v3

Bạn có:

export default {
    plugins: {
        tailwindcss: {},
        autoprefixer: {},
    },
};

Trong khi với Tailwind v4 phải là:

export default {
    plugins: {
        "@tailwindcss/postcss": {},
    },
};

Đây là nguyên nhân gây lỗi:

Cannot find module 'autoprefixer'

Sau khi sửa, npm run build chạy bình thường.


Lỗi 2: Trang About không có Tailwind

Đây không liên quan đến build.

about.blade.php chỉ có:

<h1>Trang About</h1>

<div class="bg-blue-600">
    Hello Tailwind
</div>

nên Laravel trả về đúng đoạn HTML đó, không qua layout chứa:

@vite(['resources/css/app.css', 'resources/js/app.js'])

Khi bọc bằng:

<x-app-layout>
    ...
</x-app-layout>

thì Tailwind mới được nạp và các class mới có tác dụng.


Chú ý: trang about cho mọi users truy cập nên k dùng x-app-layout (dính navbar)

Đây là quy trình đầy đủ để tạo <x-public-layout> bằng Artisan.

Bước 1. Tạo component

php artisan make:component PublicLayout

Laravel sẽ tạo:

app/
└── View/
    └── Components/
        └── PublicLayout.php

resources/
└── views/
    └── components/
        └── public-layout.blade.php

Bước 2. Sửa resources/views/components/public-layout.blade.php

Thay toàn bộ nội dung mặc định:

<div>
    <!-- An unexamined life is not worth living. - Socrates -->
</div>

thành:

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ config('app.name') }}</title>

    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-100">

    <main class="max-w-7xl mx-auto px-6 py-8">
        {{ $slot }}
    </main>

</body>
</html>

{{ $slot }} là nơi nội dung của từng trang sẽ được chèn vào.


Bước 3. Sử dụng trong view

Ví dụ resources/views/about.blade.php:

<x-public-layout>

    <h1 class="text-3xl font-bold mb-6">About</h1>

    <div class="bg-orange-600 text-white p-4 rounded-lg">
        Hello Tailwind
    </div>

</x-public-layout>

Bước 4. Route

Route::view('/about', 'about');

hoặc

Route::get('/about', function () {
    return view('about');
});

Kết quả cấu trúc

app/
└── View/
    └── Components/
        └── PublicLayout.php

resources/
└── views/
    ├── about.blade.php
    └── components/
        └── public-layout.blade.php

Đây là cách làm chuẩn khi dùng Blade Components trong Laravel 12, tương tự như các component sẵn có như <x-app-layout>, <x-guest-layout><x-input-label>.

x1

quay về MỤC LỤC

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

Đăng nhận xét

Facebook Youtube RSS