Build a High‑Performance RBAC System in Webman with Casbin and ThinkORM

This guide walks PHP developers through designing a relational database schema and implementing a role‑based access control (RBAC) system in the Webman framework by integrating Casbin for policy enforcement and ThinkORM for elegant ORM handling, complete with configuration, model definitions, and middleware.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Build a High‑Performance RBAC System in Webman with Casbin and ThinkORM

Overview

RBAC (Role‑Based Access Control) is a classic permission model that links users, roles, and permissions. By combining the Webman framework with the Casbin plugin and ThinkORM, PHP developers can quickly build an efficient RBAC system.

This combination yields concise code, strong maintainability, dynamic permission adjustments, and millisecond‑level response times, making it ideal for backend management systems.

Why Choose Webman + Casbin + ThinkORM?

RBAC model : Simple and efficient; users bind to roles, roles bind to permissions (menus or APIs), supporting multi‑role inheritance and data‑scope control.

Casbin (webman‑permission) : A Webman‑specific plugin that supports multiple access‑control models (ACL, RBAC, ABAC) with built‑in persistence (database) and seamless middleware integration.

ThinkORM : A lightweight ORM offering model relations, query building, and eager loading, perfectly suited for Webman’s high‑performance environment.

Webman framework : High‑performance, non‑blocking I/O with coroutine support, ideal for API services.

Database Design Details

The core tables implement a many‑to‑many relationship chain: user → role → menu → API, with Casbin rules storing the final policies.

1. sys_user

uid (bigint, primary key, AUTO_INCREMENT) – User ID

username (varchar(50)) – Username

password (varchar(255)) – Encrypted password

email (varchar(100)) – Email address

status (tinyint(1), default 1) – Status (1: enabled)

2. sys_role

id (bigint, primary key, AUTO_INCREMENT) – Role ID

name (varchar(50)) – Role name (e.g., "admin")

code (varchar(50)) – Role code (e.g., "admin")

data_scope (varchar(50), default 'all') – Data scope

status (tinyint(1), default 1) – Status (1: enabled)

3. sys_user_role (pivot)

user_id (bigint) – User ID

role_id (bigint) – Role ID

Composite primary key: (user_id, role_id)

4. sys_menu

id (bigint, primary key, AUTO_INCREMENT) – Menu ID

name (varchar(50)) – Menu name (English)

title (varchar(100)) – Menu title (Chinese)

type (tinyint(1), default 0) – 0: directory, 1: menu, 2: button

parent_id (bigint, default 0) – Parent menu ID (supports tree structure)

component (varchar(255)) – Front‑end component path

path (varchar(255)) – Route path

icon (varchar(100)) – Icon

sort (int(11), default 0) – Sort order

5. sys_role_menu (pivot)

role_id (bigint) – Role ID

menu_id (bigint) – Menu ID

6. sys_api

id (bigint, primary key, AUTO_INCREMENT) – API ID

name (varchar(50)) – API name

title (varchar(100)) – API title

path (varchar(255)) – API path (e.g., "/user/list")

type (varchar(10)) – HTTP method (e.g., "GET")

action (varchar(50)) – Action description (e.g., "read")

7. sys_menu_api (pivot)

menu_id (bigint) – Menu ID

api_id (bigint) – API ID

8. casbin_rule

id (bigint, primary key, AUTO_INCREMENT) – Rule ID

ptype (varchar(128)) – Policy type (e.g., "p")

v0 (varchar(128)) – Subject (e.g., role code "admin")

v1 (varchar(128)) – Object (e.g., "/user/list")

v2 (varchar(128)) – Action (e.g., "GET")

v3‑v5 (varchar(128)) – Optional extensions (e.g., data_scope)

Implementation Steps

Step 0 – Install Webman

Installation guide: https://www.workerman.net/doc/webman/install.html

Step 1 – Install Required Plugins

composer require -W workerman/webman-framework
composer require -W webman/think-orm
composer require -W casbin/webman-permission

Restart Webman after installation:

php start.php restart

Step 1.1 – Configure ThinkORM (config/thinkorm.php)

return [
    'default' => 'mysql',
    'connections' => [
        'mysql' => [
            'type' => 'mysql',
            'hostname' => env('DB_HOST', '127.0.0.1'),
            'database' => env('DB_DATABASE', 'rbac_db'),
            'username' => env('DB_USERNAME', 'root'),
            'password' => env('DB_PASSWORD', ''),
            'hostport' => env('DB_PORT', 3306),
            'charset' => 'utf8mb4',
            'prefix' => 'sys_',
        ],
    ],
];

Step 1.2 – Configure Casbin (config/plugin/casbin/webman-permission/permission.php)

<?php
/**
 * @desc permission.php description
 */
declare(strict_types=1);
return [
    'default' => 'basic',
    'log' => [
        'enabled' => false,
        'logger' => 'Casbin',
        'path' => runtime_path() . '/logs/casbin.log',
    ],
    'basic' => [
        'model' => [
            'config_type' => 'file',
            'config_file_path' => config_path() . '/plugin/casbin/webman-permission/rbac_model.conf',
            'config_text' => '',
        ],
        // Use ThinkORM adapter
        'adapter' => Casbin\WebmanPermission\Adapter\DatabaseAdapter::class,
        'database' => [
            'connection' => '',
            'rules_table' => 'casbin_rule',
            'rules_name' => null,
        ],
    ],
];

Step 1.3 – DI Container (config/container.php)

$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
return $builder->build();

Step 1.4 – Casbin Model Class (app/model/CasbinRule.php)

namespace app\model;
use think\Model;
class CasbinRule extends Model {
    protected $table = 'casbin_rule';
    public $timestamps = false;
}

Step 2 – Define ThinkORM Models

Example: SysUser.php

namespace app\model;
use think\Model;
class SysUser extends Model {
    protected $table = 'sys_user';
    protected $pk = 'uid';
    public function roles() {
        return $this->belongsToMany(SysRole::class, 'sys_user_role', 'role_id', 'user_id');
    }
    public function getRoleCodes() {
        return $this->roles()->column('code');
    }
}

SysRole.php (adds menus() relation) and SysMenu.php (adds apis() relation) follow the same pattern, using belongsToMany to link pivot tables.

Use eager loading to avoid N+1 queries, e.g.:

$role->with(['menus.apis'])->find($id);

Step 3 – Sync Permissions to Casbin (RoleController.php)

namespace app\controller;
use support\Request;
use app\model\SysRole;
use Casbin\WebmanPermission\Permission;
class RoleController {
    public function assignMenu(Request $request) {
        $roleId = $request->post('roleId');
        $menuIds = $request->post('menuIds', []);
        $role = SysRole::find($roleId);
        if (!$role) {
            return response('角色不存在', 404);
        }
        // Sync role‑menu relations
        $role->menus()->sync($menuIds);
        // Clear old policies for this role
        Permission::deletePermissionsForUser($role->code);
        // Load menus and APIs, then add new policies
        $role->load('menus.apis');
        foreach ($role->menus as $menu) {
            foreach ($menu->apis as $api) {
                Permission::addPolicy($role->code, $api->path, $api->type);
            }
        }
        return response('权限同步成功');
    }
}

Step 4 – Authorization Middleware

<?php
/**
 * @desc Authorization middleware
 */
declare(strict_types=1);
namespace app\middleware;
use Casbin\Exceptions\CasbinException;
use Casbin\WebmanPermission\Permission;
use Tinywan\ExceptionHandler\Exception\ForbiddenHttpException;
use Tinywan\ExceptionHandler\Exception\UnauthorizedHttpException;
use Tinywan\Jwt\JwtToken;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
class AuthorizationMiddleware implements MiddlewareInterface {
    public function process(Request $request, callable $handler): Response {
        $role = JwtToken::getExtendVal('role');
        if (empty($role)) {
            throw new UnauthorizedHttpException();
        }
        try {
            $url = request()->path();
            $action = request()->method();
            if (!Permission::enforce($role, $url, strtoupper($action))) {
                throw new ForbiddenHttpException();
            }
        } catch (CasbinException $e) {
            throw new ForbiddenHttpException();
        }
        return $handler($request);
    }
}

Register the middleware globally (config/middleware.php):

return [
    '' => [
        app\middleware\AuthorizationMiddleware::class,
    ],
];

Conclusion

Combining Casbin with ThinkORM in a Webman project provides a clean, highly extensible RBAC implementation that can be quickly integrated into any backend management system.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendaccess controlPHPRBACCasbinWebmanthinkorm
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.