Zend Framework cung cấp một công cụ nhẹ, mềm dẻo zend-permissions-acl để triển khai khái niệm ACL - Danh sách truy cập. Với các ACL bạn sẽ điều khiển cấp phép truy cập với các đối tượng cần bảo vệ.

Để cài đặt zend-permissions-acl dùng lệnh compose hoặc tải trực tiếp vào dự án (mã nguồn acl):

composer require zendframework/zend-permissions-acl

Có hai khái niệm bạn cần biết ở đây:

  • resource (tài nguyên): một đối tượng mà việc truy cập được giám sát, điều khiển : control, url, file ...
  • role (vai trò): một đối tượng mà có thể nó muốn truy cập tài nguyên.

Tạo Rersource, Role, Acl

Resource được tạo ra từ lớp GenericResource, và Role tạo ra từ GenericRole ví dụ để tạo tài nguyên có tên là photo và tạo ra role tên là guest làm như sau:

use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;

//Tạo ra role tên là guest
$role_guest = new Role('guest');

//Tạo ra resource tên là photo
$resource_photo = new Resource('photo');

Khởi tạo một ACL làm như sau:

$acl = new Acl();

Để thêm các Role - vài trò  vào Acl dùng lệnh như sau:

$acl->addRole($role, $parents = null)

Trong đó:

  • $role: là vai trò đã định nghĩa
  • $parents: nếu bạn sử dụng thì nó là một mảng tên các role cha mà $role kế thưa vai trò. Nghĩa là vai trò của $role được lọc qua các vái trò cha nó.

Sau khi thêm các Rerource và Role vào Acl, thì phải cho biết mỗi Role (vài trò) sẽ được phép hay không được phép truy cập vào Resource. Làm điều này bằng cách gọi lệnh deny và allow sao cho phù hợp:

$acl->allow($roles, $resources);
$acl->deny($roles, $resources);

Trong đó: 

  • $roles: là tên vài trò, hoặc đối tượng Role, hoặc một mảng tên các Role cho phép truy cập, hay cấm truy cập tài nguyên.
  • $resouces: là tên Resource, đối tượng Resource hay một mảng tên Resource.

Cuối cùng đã có Acl hoàn chỉnh, ở chỗ nào đó bạn cần kiểm tra vài trò này có được truy cập tài nguyên kia hay không thì dùng lệnh như sau:

$acl->isAllowed($role, $resource)

Ý nghĩa các tham số như trên, nó trả về true là cho phép và false là không cho phép.

Lưu ý mặc định đếu không định nghĩa allow, deny thì một role tới một tài nguyên cụ thể là trạng thái bị cấm.

Ví dụ

Tạo một Acl để kiểm tra quyền truy cập với các tài nguyên với yêu câu như sau:

  • Trang web có các chức năng (tài nguyên): view_photodelete_photoupload_photo
  • Có bốn nhóm người dùng (Role): guest, member, admin, specialuser, trong đó:
    • guest: chỉ được phép view_photo.
    • member: được phép view_photo và upload_photo.
    • admin: được phép view_photo, delete_photo, upload_photo.
    • specialuser: kế thừa quyền từ ba nhóm trên tuy nhiên bị cấm upload_photo.

Tạo file index.php trong thư mục dự án (đã có vendor cài bằng composer ở trên).

<?php

use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;

include "vendor/autoload.php";


$acl = new Acl();

$acl->addRole(new Role('guest'))
    ->addRole(new Role('member'))
    ->addRole(new Role('admin'));

$parents = array('guest', 'member', 'admin');

$acl->addRole(new Role('specialuser'), $parents);

$acl->addResource(new Resource('view_photo'));
$acl->addResource(new Resource('delete_photo'));
$acl->addResource(new Resource('upload_photo'));

//Định nghĩa cấm và cho phép với các role tới tài nguyên


$acl->allow('member', ['upload_photo','view_photo']);
$acl->allow('guest', 'view_photo');
$acl->allow('admin', ['view_photo', 'delete_photo', 'upload_photo']);

$acl->deny('specialuser', ['upload_photo']);

function testallow($role, $res) {
    global $acl;
    echo "[$role] truy cập [$res] : ", $acl->isAllowed($role, $res)?'<span style="color:red">Cho phép</span>':'Bị cấm' ,'<br>';
}

$allroles = ['guest', 'member','admin', 'specialuser'];
$allress  = ['view_photo', 'delete_photo','upload_photo'];

foreach ($allroles as $role)
{
    foreach ($allress as $res)
        testallow($role, $res);
    echo "<hr>";
}

Chạy file trên, thấy in ra thông tin về quyền từng nhóm user như sau là chính xác:

[guest] truy cập [view_photo] : Cho phép
[guest] truy cập [delete_photo] : Bị cấm
[guest] truy cập [upload_photo] : Bị cấm
[member] truy cập [view_photo] : Cho phép
[member] truy cập [delete_photo] : Bị cấm
[member] truy cập [upload_photo] : Cho phép
[admin] truy cập [view_photo] : Cho phép
[admin] truy cập [delete_photo] : Cho phép
[admin] truy cập [upload_photo] : Cho phép
[specialuser] truy cập [view_photo] : Cho phép
[specialuser] truy cập [delete_photo] : Cho phép
[specialuser] truy cập [upload_photo] : Bị cấm

Định nghĩa các quyền truy cập

Ở ví dụ trên là trường hợp cơ bản tìm hiểu về Alc, giờ xem xét các trường hợp hay gặp khác. 

Trong nhiều trường hợp bạn không nhất thiết phải định nghĩa Resource mà chỉ cần Allow, Deny các Role cho toàn bộ trang web. Ví dụ trang web có các chức năng:  view, edit, submit, revise, publish, archive, delete. Giờ bạn chỉ cần phân quyền nhóm người dùng thực hiện các chức năng đó trên website mà không cần chỉ ra là chức năng đó liên quan đến tài nguyên nào (hay nói cách khác là tất cả các tài nguyên).

Lúc đó bạn chỉ việc thêm các Role và Allow, Deny với tham số resource là null và thêm tham số tên chức năng.

use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole as Role;

$acl = new Acl();

$roleGuest = new Role('guest');
$acl->addRole($roleGuest);
$acl->addRole(new Role('staff'), $roleGuest);
$acl->addRole(new Role('editor'), 'staff');
$acl->addRole(new Role('administrator'));

// gest chỉ được xem nội dung.
$acl->allow('guest', null, 'view');

$acl->allow('staff', null, array('edit', 'submit', 'revise'));

 
$acl->allow('editor', null, array('publish', 'archive', 'delete'));

$acl->allow('administrator'); //Không chỉ ra chức năng nào cho phép có nghĩa là được phép tất cả

Tạo file vidu tên là acl.php và câp nhật nội dung như sau để kiểm tra

<?php
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;

include "vendor/autoload.php";

$acl = new Acl();

$roleGuest = new Role('guest');
$acl->addRole($roleGuest);
$acl->addRole(new Role('staff'), $roleGuest);
$acl->addRole(new Role('editor'), 'staff');
$acl->addRole(new Role('administrator'));

$acl->allow($roleGuest, null, 'view');

$acl->allow('staff', null, array('edit', 'submit', 'revise'));

$acl->allow('editor', null, array('publish', 'archive', 'delete'));

$acl->allow('administrator');

function testallow($role, $action) {
    global $acl;
    echo "[$role] thực hiện [$action] : ", $acl->isAllowed($role, null ,$action)?'<span style="color:red">Cho phép</span>':'Bị cấm' ,'<br>';
}

$allroles = ['guest', 'staff','editor', 'administrator'];
$actions  = ['view', 'edit','submit', 'revise', 'publish','archive', 'delete'];

foreach ($allroles as $role)
{
    foreach ($actions as $action)
        testallow($role, $action);
    echo "<hr>";
}

Để loại bỏ một Allow, Deny thì dùng lệnh: removeDenyremoveAllow.

Alc nâng cao

Cache, Lưu trữ Alc để dùng lại

Mỗi khi bạn cần dùng Alc, bạn phải định nghĩa các Role, Resource, Allow, Deny ... Điều này khi nhiều tài nguyên, nhiều Role có thể gây ảnh hưởng tới hiệu xuất. Nhưng rất tiếc, zend-permission-acl không cung cấp chức năng cache, lưu lại Alc mà bạn phải tự làm. Rất may là đối tượng serialized được nên bạn có thể dùng các hàm serialize và unserialize để cache lại Alc để dùng lại.

Cập nhật quyền lúc chạy bằng Assertion

Tức là bạn muốn thay đổi các quyền tùy thuộc vào ngữ cảnh nào đó. Ví dụ cấm truy cập từ một số IP, hạn chế truy cập từ giờ này đến giờ kia ... như vậy quyền không phải cố định mà biến đổi theo ngữ cảnh. Để tạo ra đối tượng đặc thù này bạn cần triển khai giao diện Assert và đưa vào tham số hàm allow quen thuộc.

allow($roles, $resources, $privileges, $assert)

Quyền này được tạo ra hàm $assert->assert() trả về TRUE. Như vậy thay vì việc phải cập nhậ Alc bạn chỉ việc cập nhật cách trả về của hàm assert().

Muốn xây dựng Assert nào thì triển khai nó theo giao diện sau:

use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Role\RoleInterface;
use Zend\Permissions\Acl\Resource\ResourceInterface;

class CleanIPAssertion implements AssertionInterface
{
    public function assert(
        Acl $acl,
        RoleInterface $role = null,
        ResourceInterface $resource = null,
        $privilege = null
    ) {
        return $this->_isCleanIP($_SERVER['REMOTE_ADDR']);
    }

    protected function _isCleanIP($ip)
    {
        // ...
    }
}

 

Đăng ký theo dõi ủng hộ kênh