外观
Foundation/Rbac 架构说明
架构概览
Foundation/Rbac/
├── Contracts/ # 接口层(依赖倒置)
│ ├── PermissionChecker.php # 权限检查器接口
│ ├── PermissionResolver.php # 权限解析器接口(策略模式)
│ └── MenuVisibilityChecker.php # 菜单可见性检查器接口
│
├── Resolvers/ # 解析器实现(V1/V2 可切换)
│ ├── SnapshotPermissionResolver.php # V1: 读取 rbac_user_permission_snapshots
│ └── RoleBasedPermissionResolver.php # V2: 读取 rbac_role_permissions(预留)
│
├── Checkers/ # 检查器实现
│ ├── DatabasePermissionChecker.php # 权限检查(依赖 Resolver)
│ └── DatabaseMenuVisibilityChecker.php # 菜单可见性检查
│
├── Concerns/ # Trait 混入(借鉴 Weiran)
│ └── HasLimsPermissions.php # User Model 混入
│
├── Middleware/
│ └── PermissionMiddleware.php # 路由权限中间件
│
├── Facades/
│ └── LimsRbac.php # Facade(静态调用)
│
├── LimsRbac.php # 核心服务(便捷 API)
└── LimsRbacServiceProvider.php # 服务提供者设计模式
1. 依赖倒置原则(DIP)
php
// ❌ 错误:直接依赖实现
class SomeService {
protected SnapshotPermissionResolver $resolver;
}
// ✅ 正确:依赖接口
class SomeService {
protected PermissionResolver $resolver; // 接口
}好处:
- 解耦:业务代码不依赖具体实现
- 可测试:可 Mock 接口
- 可扩展:可替换实现
2. 策略模式(Strategy Pattern)
php
// V1: 快照策略
app()->singleton(PermissionResolver::class, SnapshotPermissionResolver::class);
// V2: 角色策略
app()->singleton(PermissionResolver::class, RoleBasedPermissionResolver::class);用户权限来源演进:
| 阶段 | Resolver | 数据源 | 说明 |
|---|---|---|---|
| V1(当前) | SnapshotPermissionResolver | rbac_user_permission_snapshots | Legacy 镜像,sync 同步 |
| V2(切主后) | RoleBasedPermissionResolver | rbac_role_permissions + override | 纯 RBAC,动态计算 |
切换方式:
php
// config/rbac.php
'resolver' => env('RBAC_RESOLVER', SnapshotPermissionResolver::class),无需修改任何业务代码!
3. Facade 模式
php
// 通过 Facade 调用
LimsRbac::check('sample.add');
// 等价于
app('lims.rbac')->check('sample.add');
// 等价于
app(LimsRbac::class)->check('sample.add');核心流程
权限检查流程
用户请求
↓
中间件/Gate/手动检查
↓
LimsRbac / User Trait
↓
DatabasePermissionChecker::has()
↓
PermissionResolver::resolve() ← 策略可切换
↓
├─ V1: SnapshotPermissionResolver
│ └─ 读取 rbac_user_permission_snapshots
│
└─ V2: RoleBasedPermissionResolver
└─ 读取 rbac_role_permissions
↓
缓存(Redis, 10分钟)
↓
返回 ['permission_code' => bool]
↓
检查逻辑(OR/AND)
↓
返回 true/false与旧架构对比
| 维度 | 旧架构(Services/Rbac) | 新架构(Foundation/Rbac) |
|---|---|---|
| 位置 | app/Services/Rbac/ | app/Foundation/Rbac/ |
| 接口 | ❌ 无 | ✅ Contracts 目录 |
| 策略 | ❌ 硬编码快照 | ✅ Resolver 可切换 |
| Trait | ❌ 无 | ✅ HasLimsPermissions |
| Facade | ❌ 无 | ✅ LimsRbac::check() |
| Gate | ❌ 自定义中间件 | ✅ Gate::before 集成 |
| Blade | ✅ 自定义指令 | ✅ 自定义 + Laravel 原生 |
V1 → V2 演进路径
当前状态(V1)
Legacy(写源)
↓ lims:rbac-sync
rbac_user_permission_snapshots(只读镜像)
↓ SnapshotPermissionResolver
Beta 读取特点:
- Legacy 改权限后需运行 sync
- Beta 读快照,不写
- 用户权限 = 快照(静态)
切主后(V2)
Beta 管理后台(写源)
↓ 直接写
rbac_role_permissions + user_permission_overrides
↓ RoleBasedPermissionResolver
Beta 读取特点:
- 废弃 Legacy 权限管理
- Beta 直接写 RBAC 表
- 用户权限 = 角色 union + override(动态)
切换步骤
开发 V2 Resolver(已完成,预留)
RoleBasedPermissionResolver已实现
创建权限管理界面(未来)
- 角色管理 CRUD
- 用户分配角色
- 用户独立权限 override(可选)
数据迁移
- 停止 Legacy 改权限
- 将快照数据迁移为角色分配
切换配置
envRBAC_RESOLVER=App\Foundation\Rbac\Resolvers\RoleBasedPermissionResolver废弃 Legacy
- 停止
lims:rbac-sync - 归档
users.{qx}列
- 停止
Laravel Gate 集成
工作原理
php
// LimsRbacServiceProvider::bootGateIntegration()
Gate::before(function ($user, $ability) {
if (! config('lims_rbac.enforce')) {
return null; // 不拦截
}
// RBAC 只接管小写点式权限,段内使用 kebab-case;单段 ability 留给 Policy。
if (preg_match(
'/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*)+$/',
$ability
) !== 1) {
return null;
}
// 超管短路封装在 Checker 内,且只会作用于已确认的 RBAC ability。
return app(PermissionChecker::class)->has((int) $user->id, $ability);
});Laravel 原生方法自动生效
php
// 控制器
$this->authorize('sample.add');
// Blade
@can('sample.add')
<button>添加</button>
@endcan
// 代码
Gate::allows('sample.add')
Gate::denies('sample.add')view、update、approve 等单段 ability 不会进入 RBAC,继续由对应 Laravel Policy 处理;权限是否存在或是否启用属于 PermissionChecker 的授权结果, 不用于猜测 ability 归属。
缓存策略
缓存键格式
V1: lims_rbac:snapshot:user:{userId}:permissions
V2: lims_rbac:role_based:user:{userId}:permissions缓存时长
- 默认:10 分钟
- 配置:
RBAC_CACHE_TTL_MINUTES
缓存清除时机
用户权限变更后
php$user->clearPermissionCache(); LimsRbac::clearUserCache($user);角色权限变更后(V2)
php// 需清除该角色所有用户的缓存 $role->users()->each(fn($user) => $user->clearPermissionCache());同步后
php// lims:rbac-sync 命令执行后 Cache::flush(); // 或批量清除
性能优化
1. 批量检查
php
// ❌ 慢:循环单次检查
foreach ($permissions as $perm) {
if (LimsRbac::check($perm)) {
// ...
}
}
// ✅ 快:批量检查
$results = LimsRbac::batchCheck($permissions);
foreach ($results as $perm => $has) {
if ($has) {
// ...
}
}2. 缓存预热
php
// 系统启动后预热热点用户
$hotUserIds = [1, 2, 3, 4, 5];
$resolver = app(PermissionResolver::class);
foreach ($hotUserIds as $userId) {
$resolver->resolve($userId); // 触发缓存
}3. 超管短路
php
// 超管判断优先,避免数据库查询
if ($checker->isSuperAdmin($userId)) {
return true;
}扩展点
1. 自定义 Resolver
php
class LdapPermissionResolver implements PermissionResolver {
public function resolve(int $userId): array {
// 从 LDAP 读取权限
}
}2. 自定义 Checker
php
class CachedPermissionChecker implements PermissionChecker {
// 实现自定义缓存逻辑
}3. 自定义中间件
php
class ApiPermissionMiddleware {
// 针对 API 的权限检查逻辑
}测试建议
单元测试
php
public function test_snapshot_resolver_resolves_user_permissions()
{
$resolver = new SnapshotPermissionResolver();
$permissions = $resolver->resolve(1);
$this->assertIsArray($permissions);
$this->assertArrayHasKey('sample.add', $permissions);
}集成测试
php
public function test_user_can_check_permission_via_trait()
{
$user = User::factory()->create();
$this->assertTrue($user->hasPermission('sample.add'));
$this->assertFalse($user->hasPermission('admin_only'));
}总结
Foundation/Rbac 架构实现了:
- ✅ 标准 RBAC 模型:User - Role - Permission
- ✅ 接口分离:依赖抽象,易扩展
- ✅ 策略可切换:V1 快照 → V2 角色,无缝升级
- ✅ Laravel 集成:Gate + Blade 原生支持
- ✅ 便捷 API:Trait + Facade 多种用法
- ✅ 高性能:缓存 + 批量检查 + 超管短路
核心优势:切换 Resolver 无需改业务代码,配置切换即可演进!