Laravel 使用 Entrust 實現 RBAC(二)

river_bird發表於2018-07-10

1、建立角色並進行分配

<?php
namespace App;

use Illuminate\Support\Facades\Log;
use Zizaco\Entrust\EntrustRole;

class Role extends EntrustRole
{
    /**
     * 建立角色
     * @param $name
     * @param $display_name
     * @param $description
     * @return bool
     * @throws \Exception
     */
    protected function createRole($name,$display_name,$description){

        try {
            $role = new Role();
            $role->name = $name;
            $role->display_name = $display_name;
            $role->description = $description;
            $role->save();
        }catch (\Exception $e){
            Log::info($e->getMessage());
            throw new \Exception('建立角色失敗');
        }
        return true;
    }

    /**
     * 給使用者新增角色
     * @param $userId
     * @param $roleId
     * @return bool
     * @throws \Exception
     */
    protected function distributeRole($userId,$roleId){

        try{
            $user = User::find($userId);
            $user->attachRole($roleId);
        }catch (\Exception $e){
            Log::info($e->getMessage());
            throw new \Exception('角色分配失敗');
        }
        return true;
    }

}

2、建立許可權並且分配到對應的角色

<?php

namespace App;
use Zizaco\Entrust\EntrustPermission;

class Permission extends EntrustPermission
{
    /**
     * 建立許可權
     * @param $name
     * @param $displayName
     * @param $description
     * @param $roleId
     * @return bool
     */
    protected function createPermission($name , $displayName , $description , $roleId){

        try {
            $permission = new Permission();
            $permission->name = $name;
            $permission->display_name = $displayName;
            $permission->description = $description;
            $permission->save();
            $role = Role::find($roleId);
            if($role){
                $role->attachPermission($permission);
            }
        }catch (\Exception $e){
            return false;
        }
        return true;
    }

}

2、檢查角色&許可權

$user->hasRole('owner'); // false
$user->hasRole('admin'); // true
$user->can('edit-user'); // true
$user->can('create-post'); // true

hasRole()和can都可以接收陣列形式的角色和許可權進行檢查:

$user->hasRole(['owner', 'admin']); // true
$user->can(['edit-user', 'create-post']); // true

預設情況下,如果使用者擁有以上任意一個角色或許可權都會返回true,如果你想檢查使用者是否擁有所有角色及許可權,可以傳遞true作為第二個引數到相應方法:

$user->hasRole(['owner', 'admin']); // true
$user->hasRole(['owner', 'admin'], true); // false
$user->can(['edit-user', 'create-post']); // true
$user->can(['edit-user', 'create-post'], true); // false

可以通過Entrust門面檢查當前登入使用者是否擁有指定角色和許可權:

Entrust::hasRole('role-name');
Entrust::can('permission-name');

可以通過萬用字元的方式來檢查使用者許可權:

// match any admin permission
$user->can("admin.*"); // true

// match any permission about users
$user->can("*_users"); // true

3、ability方法
還可以通過使用ability方法來實現更加高階的檢查,這需要三個引數(角色、許可權、選項),同樣角色和許可權既可以是字串也可以是陣列:
$user->ability(array('admin', 'owner'), array('create-post', 'edit-user'));
// 或者
$user->ability('admin,owner', 'create-post,edit-user');
這將會檢查使用者是否擁有任意提供的角色和許可權,在本例中會返回true,因為使用者的角色是admin並且擁有create-post許可權。

第三個引數是一個可選陣列:

$options = array(
    'validate_all' => true | false (Default: false),
    'return_type' => boolean | array | both (Default: boolean)
);

其中validate_all是一個布林值,用於設定是否檢查所有值;return_type用於指定返回布林值、檢查值陣列還是兩者都有。

下面是一個輸出示例:

$options = array(
    'validate_all' => true,
    'return_type' => 'both'
);

list($validate, $allValidations) = $user->ability(
    array('admin', 'owner'),
    array('create-post', 'edit-user'),
    $options
);

var_dump($validate);
// bool(false)

var_dump($allValidations);
// array(4) {
//     ['role'] => bool(true)
//     ['role_2'] => bool(false)
//     ['create-post'] => bool(true)
//     ['edit-user'] => bool(false)
// }

同樣,Entrust門面也為當前登入使用者提供了ability方法:

Entrust::ability('admin,owner', 'create-post,edit-user');
// 等價於
Auth::user()->ability('admin,owner', 'create-post,edit-user');

4、Blade模板
在Blade模板中也可以使用上述三個方法對應的指令:

@role('admin')
    <p>This is visible to users with the admin role. Gets translated to
    \Entrust::role('admin')</p>
@endrole

[@permission](https://learnku.com/users/3492)('manage-admins')
    <p>This is visible to users with the given permissions. Gets translated to
    \Entrust::can('manage-admins'). The [@can](https://learnku.com/users/12729) directive is already taken by core
    laravel authorization package, hence the [@permission](https://learnku.com/users/3492) directive instead.</p>
@endpermission

@ability('admin,owner', 'create-post,edit-user')
    <p>This is visible to users with the given abilities. Gets translated to
    \Entrust::ability('admin,owner', 'create-post,edit-user')</p>
@endability

5、中介軟體
你可以在中介軟體中通過角色或許可權來過濾路由以及路由群組:

Route::group(['prefix' => 'admin', 'middleware' => ['role:admin']], function() {
    Route::get('/', 'AdminController@welcome');
    Route::get('/manage', [
        'middleware' => ['permission:manage-admins'], 
        'uses' => 'AdminController@manageAdmins'
    ]);
});

支援OR/AND邏輯:

'middleware' => ['role:admin|root']
'middleware' => ['permission:owner', 'permission:writer']

同樣可以使用ability中介軟體:
'middleware' => ['ability:admin|owner,create-post|edit-user,true']

6、路由過濾器的快捷實現
通過許可權和角色過濾路由你還可以在app/Http/routes.php呼叫如下程式碼:

// 只有擁有'create-post'許可權對應角色的使用者才能訪問admin/post*
Entrust::routeNeedsPermission('admin/post*', 'create-post');

// 只有所有者才能訪問admin/advanced*
Entrust::routeNeedsRole('admin/advanced*', 'owner');

// 第二個引數可以是陣列,這樣使用者必須滿足所有條件才能訪問對應路由
Entrust::routeNeedsPermission('admin/post*', array('create-post', 'edit-comment'));
Entrust::routeNeedsRole('admin/advanced*', array('owner','writer'));

以上方法都可以接受第三個引數,如果第三個引數為空那麼禁止訪問預設呼叫App::abort(403),否則會返回第三個引數,所以我們可以這麼做:

Entrust::routeNeedsRole('admin/advanced*', 'owner', Redirect::to('/home'));
甚至還可以接受第四個引數,該引數預設為true,意味著將會檢查提供的所有角色和許可權,如果你將其設定為false,則該方法只有在所有角色/許可權都不滿足才會返回失敗:

// 如果使用者擁有'create-post'、'edit-comment'其中之一或者兩個許可權都具備則可以訪問
Entrust::routeNeedsPermission('admin/post*', array('create-post', 'edit-comment'), null, false);

// 如果使用者是所有者、作者或者都具備則可以訪問
Entrust::routeNeedsRole('admin/advanced*', array('owner','writer'), null, false);

Entrust::routeNeedsRoleOrPermission(
    'admin/advanced*',
    array('owner', 'writer'),
    array('create-post', 'edit-comment'),
    null,
    false
);

7、路由過濾器
Entrust角色/許可權可通過呼叫Entrust門面上的can和hasRole方法應用在過濾器中:

Route::filter('manage_posts', function()
{
    // check the current user
    if (!Entrust::can('create-post')) {
        return Redirect::to('admin');
    }
});

// 只有使用者對應角色擁有 'manage_posts' 許可權才能訪問任意 admin/post 路由
Route::when('admin/post*', 'manage_posts');
使用過濾器來檢查角色:

Route::filter('owner_role', function()
{
    // check the current user
    if (!Entrust::hasRole('Owner')) {
        App::abort(403);
    }
});

// 只有所有者才能訪問 admin/advanced 路由
`Route::when('admin/advanced
', 'owner_role');`
正如你所看到的,Entrust::hasRole和Entrust::can方法檢查使用者是否登入、然後才去判斷他們是否有用指定角色或許可權,如果使用者沒有登入則直接返回false。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章