Overview
Implement role-based access control in Laravel using native Eloquent relationships.
Originally published on Jang Keyte's Blog.
This article covers Simple Role & Permission in Laravel Without Packages — practical notes from real-world web development experience.
Code Examples
composer create-project --prefer-dist laravel/breeze New_Project
DB_DATABASE= laravel //tên database
DB_USERNAME= root //username
DB_PASSWORD= //password
php artisan make:model Permission -m
php artisan make:model Role -m
Read More
For the full Vietnamese version, switch language using the VI | EN toggle above, or visit the original post.
**Bước 1:**Tạo dự án Laravel, nếu đã có dự án sẵn có thể bỏ qua bước này
composer create-project --prefer-dist laravel/breeze New_Project
Cập nhật file**.envđể kết nốidatabase**
DB_DATABASE= laravel //tên database
DB_USERNAME= root //username
DB_PASSWORD= //password
Bước 2:TạomodelvàmigrationcủaRolevàPermission
php artisan make:model Permission -m
php artisan make:model Role -m
**Bước 3:**Cập nhật các file migration
BảngUser
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
BảngRole
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
BảngPermission
public function up()
{
Schema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
Bước 4:Tạo pivot tableusers_permissionsđể kết nối cácrolevàpermission
php artisan make:migration create_users_permissions_table --create=users_permissions
Cập nhật nội dung file migrate củausers_permissions
public function up()
{
Schema::create('users_permissions', function (Blueprint $table) {
$table->unsignedInteger('user_id');
$table->unsignedInteger('permission_id');
//FOREIGN KEY
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
//PRIMARY KEYS
$table->primary(['user_id','permission_id']);
});
}
Tạo bảng pivot tableusers_rolesđể phân quyềnrolechouser
php artisan make:migration create_users_roles_table --create=users_roles
Cập nhật nội dung file migrate củausers_roles
public function up()
{
Schema::create('users_roles', function (Blueprint $table) {
$table->unsignedInteger('user_id');
$table->unsignedInteger('role_id');
//FOREIGN KEY
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
//PRIMARY KEYS
$table->primary(['user_id','role_id']);
});
}
Tạo bảng pivot tableroles_permissionsđể phân quyềnpermissionchouser. Ví dụ,usercó quyền xem đối với bài đăng còn với tư cách làadmincó quyền chỉnh sửa hoặc xóa bài đăng
php artisan make:migration create_roles_permissions_table --create=roles_permissions
Cập nhật nội dung file migrate củaroles_permissions
public function up()
{
Schema::create('roles_permissions', function (Blueprint $table) {
$table->unsignedInteger('role_id');
$table->unsignedInteger('permission_id');
//FOREIGN KEY
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
$table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
//PRIMARY KEYS
$table->primary(['role_id','permission_id']);
});
}
Chạy câu lệnh sau để tự động tạo database
php artisan migrate
Bước 5:Tạorelationships
Tạo quan hệ giữa hai bảngrolesvàpermissions
Cập nhật fileApp/Role.php
public function permissions() {
return $this->belongsToMany(Permission::class,'roles_permissions');
}
public function users() {
return $this->belongsToMany(User::class,'users_roles');
}
Cập nhật fileApp/Permission.php
public function roles() {
return $this->belongsToMany(Role::class,'roles_permissions');
}
public function users() {
return $this->belongsToMany(User::class,'users_permissions');
}
**Bước 6:**Tạo Traits
Tạo traitsHasPermissionsTrait.phpđặt trong thư mụcapp\Traitsvới nội dung sau
namespace App\Traits;
use App\Models\Permission;
use App\Models\Role;
trait HasPermissionsTrait {
public function givePermissionsTo(... $permissions) {
$permissions = $this->getAllPermissions($permissions);
if($permissions === null) {
return $this;
}
$this->permissions()->saveMany($permissions);
return $this;
}
public function withdrawPermissionsTo( ... $permissions ) {
$permissions = $this->getAllPermissions($permissions);
$this->permissions()->detach($permissions);
return $this;
}
public function refreshPermissions( ... $permissions ) {
$this->permissions()->detach();
return $this->givePermissionsTo($permissions);
}
public function hasPermissionTo($permission) {
return $this->hasPermissionThroughRole($permission) || $this->hasPermission($permission);
}
public function hasPermissionThroughRole($permission) {
foreach ($permission->roles as $role){
if($this->roles->contains($role)) {
return true;
}
}
return false;
}
public function hasRole( ... $roles ) {
foreach ($roles as $role) {
if ($this->roles->contains('slug', $role)) {
return true;
}
}
return false;
}
public function roles() {
return $this->belongsToMany(Role::class,'users_roles');
}
public function permissions() {
return $this->belongsToMany(Permission::class,'users_permissions');
}
protected function hasPermission($permission) {
return (bool) $this->permissions->where('slug', $permission->slug)->count();
}
protected function getAllPermissions(array $permissions) {
return Permission::whereIn('slug',$permissions)->get();
}
}
}
Sử dụng traits cho modelUsertrongapp\Models\User.php
namespace App\Models;
use App\Traits\HasPermissionsTrait;
class User extends Authenticatable
{
use HasPermissionsTrait; //Import The Trait
}
**Bước 7:**Tạo custom Provider
Để có thể sử dụng lệnh Laravel directive “can”để kiểm tra xem User có quyền nào đó hay không$user->can()thay vì sử dụng hàm$user->hasPermissionTo(). Tạo mới providerPermissionsServiceProviderđể ủy quyền.
php artisan make:provider PermissionsServiceProvider
Đăng ký trong methodbootnhư sau:
public function boot()
{
try {
Permission::get()->map(function ($permission) {
Gate::define($permission->slug, function ($user) use ($permission) {
return $user->hasPermissionTo($permission);
});
});
} catch (\Exception $e) {
report($e);
return false;
}
//Blade directives
Blade::directive('role', function ($role) {
return "if(auth()->check() && auth()->user()->hasRole({$role})) :"; //return this if statement inside php tag
});
Blade::directive('endrole', function ($role) {
return "endif;"; //return this endif statement inside php tag
});
}
Tiếp tục đăng kýPermissionsServiceProvidertrongconfig\app.php
'providers' => [
App\Providers\PermissionsServiceProvider::class,
],
**Bước 8:**Tạo dữ liệu để test
Đăng ký route để tạo mớiuser, roler, permissionvà các bảng pivot liên quan
Route::get('/roles', [PermissionController::class, 'create']);
Xử lý trongApp\Http\Controller\PermissionController
namespace App\Http\Controllers;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use Illuminate\Http\Request;
class PermissionController extends Controller
{
public function Permission()
{
$user_permission = Permission::where('slug','create-tasks')->first();
$admin_permission = Permission::where('slug', 'edit-users')->first();
//RoleTableSeeder.php
$user_role = new Role();
$user_role->slug = 'user';
$user_role->name = 'Người dùng';
$user_role->save();
$user_role->permissions()->attach($user_permission);
$admin_role = new Role();
$admin_role->slug = 'admin';
$admin_role->name = 'Quản trị viên';
$admin_role->save();
$admin_role->permissions()->attach($admin_permission);
$user_role = Role::where('slug','user')->first();
$admin_role = Role::where('slug', 'admin')->first();
$createTasks = new Permission();
$createTasks->slug = 'create-tasks';
$createTasks->name = 'Create Tasks';
$createTasks->save();
$createTasks->roles()->attach($user_role);
$editUsers = new Permission();
$editUsers->slug = 'edit-users';
$editUsers->name = 'Edit Users';
$editUsers->save();
$editUsers->roles()->attach($admin_role);
$user_role = Role::where('slug','user')->first();
$admin_role = Role::where('slug', 'admin')->first();
$user_perm = Permission::where('slug','create-tasks')->first();
$admin_perm = Permission::where('slug','edit-users')->first();
$user = new User();
$user->name = 'Test_User';
$user->email = 'test_user@gmail.com';
$user->password = bcrypt('1234567');
$user->save();
$user->roles()->attach($user_role);
$user->permissions()->attach($user_perm);
$admin = new User();
$admin->name = 'Test_Admin';
$admin->email = 'test_admin@gmail.com';
$admin->password = bcrypt('admin1234');
$admin->save();
$admin->roles()->attach($admin_role);
$admin->permissions()->attach($admin_perm);
return redirect()->back();
}
}
Truy cập vào route url để tạo dữ liệuhttp://localhost/roles, có thể xử lý dữ liệu bằng các cách sau:
$user = $request->user();
dd($user->hasRole('user')); //sẽ return true, nếu user có role
dd($user->givePermissionsTo('create-tasks'));// sẽ return về permission, không thì trả về null
dd($user->can('create-tasks')); // sẽ return true, nếu user có permission
Tương tự khi sử dụng trongblade
@role('user')
This is user role
@endrole
@role('admin')
This is admin role
@endrole
**Bước 9:**Cài đặt trong Middleware
Đăng ký mới mộtmiddleware
php artisan make:middleware RoleMiddleware
Xử lýRoleMiddlewaređể checkrolevàpermission
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle($request, Closure $next, $role, $permission = null)
{
$roles = explode("|", $role);
$can_access = false;
foreach ($roles as $user_role) {
if($request->user()->hasRole($user_role)) {
$can_access = true;
}
}
if($permission != null && $request->user()->can($permission)) {
$can_access = true;
}
return $can_access ? $next($request) : abort(401);
}
}
Đăng kýRoleMiddlewarevào trongApp\Http\Kernel.php
protected $routeMiddleware = [
'role' => \App\Http\Middleware\RoleMiddleware::class,
];
Có thể sử dụng role trongmidlewaređể check quyền hạn user
Route::group(['middleware' => ['web', 'role:admin|user']], function() {
Route::get('/user', function() {
return 'Welcome...!!';
});
});
Có thể sử dụng trực tiếp trongController
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request)
{
if ($request->user()->can('create-tasks')) {
...
}
}
public function destroy(Request $request, $id)
{
if ($request->user()->can('delete-tasks')) {
...
}
}