Hướng dẫn phân quyền với laravel

  • February 3, 2021
  • 6995

Yêu cầu đặt ra là tạo một trang admin có chức năng phân quyền, các role cơ bản: super-admin, admin, client ... tương ứng với mỗi role sẽ có các quyền(permissions) khác nhau. Để giải quyết yêu cầu này hôm nay chúng ta cùng tìm hiểu laravel-permission một package phân quyền nhanh và đơn giản viết cho laravel framework.

Cài đặt laravel permission

Laravel permission cho phép chúng ta có thể dễ dàng phân chia các vai trò (roles) và quyền (permissions) dùng để quản lý quyền hạn truy cập trong một trang web.

Các bước cài đặt

1. Sử dụng laravel phiên bản lớn hơn hơn 5.8
2. Nếu source code đã có file config/permission.php bạn phải đổi tên hoặc xóa nó đi, bởi vì khi setup thì package tạo ra file config/permission.php
3. Cài đặt package với composer


composer require spatie/laravel-permission

4. Đăng kí service provider: service provider sẽ được đăng kí tự động or đăng kí bằng tay tuỳ phiên bản laravel, đăng kí service provider trong config/app.php


'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];

5. Tạo migration và config permisstion


php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

6. Clear config cache


php artisan optimize:clear
 # or
 php artisan config:clear

7. Tạo các bảng phân quyền


php artisan migrate

Khi chạy xong command trên các bảng sau sẽ được tạo ra:


roles: Quản lí các vai trò của người dùng(nhóm quyền): super-admin, admin, client
permissions: Quản lí các quyền truy cập vào ứng dụng: list-user, create_user, show_user, update_user, delete_user ...
role_has_permissions: Quản lí các quyền được gán cho 1 role
model_has_roles: Quản lí các role được gán cho 1 user
model_has_permissions: Quản lí các quyền được gán cho 1 user

8. Thêm trait phân quyền cho model sử dụng đăng nhập admin


use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // ...
}

Như vậy là bạn đã cài đặt và config xong package laravel permission rồi đó, thật đơn giản phải không, giờ thì sử dụng nó thôi nào :D

Sử dụng laravel permisstion

1. Các hàm tạo, gán role và permission

1. Tạo role, permission


use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

$role = Role::create(['name' => 'writer']);
$permission = Permission::create(['name' => 'edit articles']);

2. Gán permission cho role


$role->givePermissionTo($permission);
$permission->assignRole($role);

3. Đồng bộ hoá permissions cho role

Các permission được gán cho role đúng bằng mảng các permission truyền vào, không bị lặp lại permission khi gán nhiều lần.


$role->syncPermissions($permissions);
$permission->syncRoles($roles);

4. Xoá quyền khỏi role


$role->revokePermissionTo($permission);
$permission->removeRole($role);

5. Gán role cho user


$user->assignRole('writer');

// You can also assign multiple roles at once
$user->assignRole('writer', 'admin');
// or as an array
$user->assignRole(['writer', 'admin']);

6. Đồng bộ hoá role cho user

Các role được gán cho user đúng bằng mảng các role truyền vào, không bị lặp lại role khi gán nhiều lần.


$user->syncRoles(['super-admin', 'admin']);

7. Xoá role khỏi user


$user->removeRole('writer');

8. Gán permission trực tiếp cho user


$user->givePermissionTo($permission);
$user->givePermissionTo(array $permissions);

9. Xoá permission khỏi user


$user->revokePermissionTo($permission);

10. Đồng bộ permission với user


$user->syncPermissions(array $permissions);

2. Các hàm lấy role, permission


// Get roles of user
$user->getRoleNames();

// Get permission thông qua role
$user->getPermissionsViaRoles()->pluck('name')->toArray();

// Get all permissions asign directly to user
$permissions = $user->getDirectPermissions();

// Get all permissions asign to user
$permissions = $user->getAllPermissions();

3. Các hàm kiểm tra permission


// check user can edit articles
$user->can('edit articles');

// Check permission in blade
@can('edit articles')
...
@endcan

// Check user has permission
$user->hasPermissionTo($permissionName);
$user->hasPermissionTo(int $permissionId);

// Kiểm tra user có 1 trong các quyền
$user->hasAnyPermission(['edit articles', 'publish articles', 'unpublish articles']);

// Kiểm tra user phải có tất cả các quyền
$user->hasAllPermissions(['edit articles', 'publish articles', 'unpublish articles']);

4. Kiểm tra permission với middleware

Default middleware


// Chỉ cho phép user có quyền publish articles được truy cập
Route::group(['middleware' => ['can:publish articles']], function () {
    //
});

Package middleware thêm các middleware vào file app/Http/Kernel.php để sử dụng


protected $routeMiddleware = [
    // ...
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];

// Chỉ user có role super-admin mới được truy cập
Route::group(['middleware' => ['role:super-admin']], function () {
    //
});

// Chỉ user có quyền publish articles mới được truy cập
Route::group(['middleware' => ['permission:publish articles']], function () {
    //
});

Viết một ứng dụng phân quyền

Giờ chúng ta cùng viết 1 ứng dụng phân quyền nhé

1. Seeding user, role, permission

1. Tạo file config các role permission


<?php
return [
    'super_admin' => [
        'roles' => [
            'name' => 'super-admin',
        ],
    ],
    'admin' => [
        'roles' => [
            'name' => 'admin',
        ],
        'permissions' => [
            'list_article',
            'show_article',
            'update_article',
            'list_image',
            'show_image',
            'update_image',
            'create_image',
            'delete_image',
        ],
    ],
    'client' => [
        'roles' => [
            'name' => 'client',
        ],
        'permissions' => [
            'list_article',
            'list_image',
            'show_image',
            'update_image',
            'create_image',
            'delete_image',
        ],
    ],
];

2. Tạo seeding user role super-admin


<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use App\Models\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $countUser = User::count();
        if (!$countUser) {
            // Create user
            $user = User::create(['name' => 'super-admin']);
            // Create role super_admin
            Role::create(config('roles.super_admin.roles'));
            // Assign role super admin to user
            $user->assignRole(config('roles.super_admin.roles'));
        }
    }
}

3. Seeding tạo các role admin, client và gán các quyền tương ứng cho role


<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class PermissionSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // Add admin role
        $adminRole = Role::where('name', config('roles.admin.roles'))->first();
        if (!$adminRole) {
            $adminRole = Role::create(config('roles.admin.roles'));
        }
        // Get new admin permission
        $newPermissions = $this->getNewPermissions('roles.admin.permissions');
        Permission::insert($newPermissions);
        $adminRole->syncPermissions(config('roles.admin.permissions'));
        
        // Add client role
        $clientRole = Role::where('name', config('roles.client.roles'))->first();
        if (!$clientRole) {
            $clientRole = Role::create(config('roles.client.roles'));
        }
        // Get new client permission
        $newPermissions = $this->getNewPermissions('roles.client.permissions');
        Permission::insert($newPermissions);
        $clientRole->syncPermissions(config('roles.client.permissions'));
    }

    /**
     * Get list new permissions from file config
     * @param string $config
     * @return array
     */
    private function getNewPermissions(string $config): array
    {
        $currentPermissions = Permission::get('name')->pluck('name')->toArray();
        $newPermissions = [];
        $configPermissions = config($config);
        foreach ($configPermissions as $configPermission) {
            if (!in_array($configPermission, $currentPermissions)) {
                $newPermissions[] = [
                    'name' => $configPermission,
                    'guard_name' => config('auth.guards.admin'),// config guard for admin 
                    'created_at' => now(),
                    'updated_at' => now(),
                ];
            }
        }

        return $newPermissions;
    }
}

như vậy là đã seeding xong user, role, permission nhé

2. Viết route check permission


// Kiểm tra quyền thêm
$api->post('articles', 'ArticlesController@store')->middleware('permission:create_article');
// Kiểm tra quyền xem list
$api->get('articles', 'ArticlesController@index')->middleware('permission:list_article');
// Kiểm tra quyền xem chi tiết
$api->get('articles/{id}', 'ArticlesController@show')->middleware('permission:show_article');
// Kiểm tra quyền xoá
$api->delete('articles/{id}', 'ArticlesController@destroy')->middleware('permission:delete_article');
// Kiểm tra quyền sửa        
$api->put('articles/{id}', 'ArticlesController@update')->middleware('permission:update_article');

Như vậy là chúng ta đã tìm hiểu và demo xong một ứng dụng phân quyền nhỏ, chúc các bạn phân quyền thành công :D