Skip to content

RBAC 核心算法

实现须与本文一致;改算法同步更新单测。表结构见数据模型,verify 菜单项用下文算法 B。


PermissionChecker

php
interface PermissionCheckerInterface
{
    public function has(string $permissionCode, ?int $userId = null): bool;
    public function hasAny(array $codes, ?int $userId = null): bool;
    public function isSuperAdmin(?int $userId = null): bool;
    public function canViewMenu(int $legacyMenuId, ?int $userId = null): bool;
}
实现用途
DatabasePermissionChecker读 RBAC 表
LegacySessionPermissionChecker$u[$code],仅 sync 期间 A/B 对比

中间件:PermissionMiddleware,用法 ->middleware('permission:site_manage')


A. has(code)

输入:userId(默认当前)、permissionCode
数据:usersuser_permission_snapshotspermissions

步骤条件结果
A0user.id == 1user.admin == 1true
A1permission 不存在或未激活false + log
A1b(后期)按 code 未命中时可查 std_code落到同一 permission_id
A3user_permission_snapshots.has_permissiontrue/false
A4无快照行false + log rbac.permission_snapshot_missing
php
function has(string $code, ?int $userId = null): bool
{
    $user = resolveUser($userId);
    if (isSuperAdmin($user)) {
        return true;
    }
  // 第一版:仅 permissions.code;后期可增加 std_code 解析
    $perm = permissions.active(code: $code);
    if ($perm === null) {
        return false;
    }
    $snapshot = user_permission_snapshots.find(user_id, permission_id);
    if ($snapshot === null) {
        logWarning('rbac.permission_snapshot_missing', ...);
        return false;
    }
    return (bool) $snapshot->has_permission;
}

对齐 legacy 的 $u[$qx]==1;不用 role_permissions 直接鉴权。


B. canViewMenu(legacyMenuId)

menu.useing != 1 → false。

取该 menu 的 menu_permissions 规则集 R(按 sort):

优先级规则判定
B0R 为空false + log
B1empty / allow_alltrue
B2display_hiddenfalse(beta 不出 display/隐藏)
B3admin / require_super_admin仅超管 true
B4deny_if_user_has有该权限 → false(如 !audit_test)
B5require_any任一 has → true

组合语义(对齐 get_menu.php):

IF allow_all in R → visible
ELSE IF display_hidden in R → hidden
ELSE IF require_super_admin AND isSuperAdmin → visible
ELSE IF any deny_if_user_has matches → hidden
ELSE IF any require_any has(permission.code) → visible
ELSE → hidden

第一版不覆盖:site_type 动态子项、shuju_tongjipenetration_test()、父级无可见子项自动收起。


C. PermissionMiddleware

permission:site_manage     → has('site_manage')
permission:site_manage,cy  → hasAny(OR)
permission:site_manage&cy  → 全部 has(AND)

RBAC_ENFORCE=true 才生效;默认 false。


缓存

  • Key:LimsRbacDef::userPermissionSnapshotCacheKey($userId)
  • TTL:RBAC_CACHE_TTL_MINUTES(默认 10)
  • sync 后 flush 该 user 或 LimsRbacDef::cacheTag($fzxId)

单测

范围用例
A0–A4超管、inactive、快照缺失
B0–B5空 qx、display、admin、!audit_test、require_any
C中间件 403/200;OR/AND 参数

Feature:试点路由 + sync 后改权限。