rbac在c#中需规范建模与缓存:rolepermission必须用中间表而非逗号分隔字符串;按钮权限应登录时预加载至hashset缓存;自定义[permission]特性须正确注册授权策略;菜单树需递归过滤并按sortorder排序。

RBAC 在 C# 里不是加个 Role 字段就能跑通的,核心崩点往往在数据建模和运行时缓存策略上——模型错一层,后期改起来比重写还疼。
RolePermission 表为什么不能存逗号分隔的权限字符串
常见错误是把权限列表塞进一个 Permissions 字段,比如 "user:read,user:edit,order:delete"。这会导致三个硬伤:
- 数据库无法对单个权限做索引或原子增删,查“谁有
order:delete权限”只能全表扫描 + 字符串匹配 - SQL 查询逻辑变脆弱,
LIKE '%order:delete%'会误匹配order:deleted_log - ORM(如 EF Core)映射困难,每次读写都要手动拆合,极易引入空格、大小写、重复项等脏数据
正确做法是建独立中间表:RolePermission,只含 RoleId、ResourceType(如 "button" 或 "menu")、ResourceId 三字段,并加联合唯一索引。
按钮权限校验为什么别每次查数据库
用户点击一个按钮就触发一次 DB 查询,延迟叠加后体验明显变卡,尤其高并发场景。更糟的是,权限变更后缓存不一致会直接导致越权或拒访。
- 登录成功后,一次性查出该用户所有有效
ButtonCode(如"user:delete"、"report:export"),存入HashSet<string></string> - 缓存键建议用
$"permissions:{userId}",过期时间设为 30 分钟,配合登录态刷新机制 - 前端按钮显隐、后端接口拦截,统一走
permissionsSet.Contains("user:delete")判断,O(1) 时间复杂度
[Permission] 特性怎么绕过 ActionFilter 被忽略的问题
自定义 [Permission("user:edit")] 特性如果注册顺序不对,中间件根本不会触发——这是 ASP.NET Core 授权链里最隐蔽的坑。
- 必须在
Program.cs中先调用builder.Services.AddAuthorization(),再注册自定义策略 - 策略注册代码(如
options.AddPolicy("HasPermission", p => p.RequireAssertion(...)))要早于app.UseAuthentication()和app.UseAuthorization() - 特性内部不要自己查数据库,而是委托给已注册的策略,靠
IAuthorizationService统一调度
菜单递归过滤为什么总漏掉子节点
返回菜单树时出现“父菜单显示但子菜单全空”,通常不是权限没配,而是过滤逻辑没穿透到叶子层级。
- 不能只检查当前菜单是否有权限,得递归判断:只要某节点自身或其任意后代节点有权限(菜单或按钮),该节点就应保留
- SQL 层推荐用 CTE 递归查询所有菜单 ID,再用
IN批量查这些 ID 对应的权限状态,避免 JOIN 导致父节点被连带过滤 - 后端组装完树结构后,务必按
SortOrder字段升序排列,别依赖前端排序——不同环境对null或缺失值的排序行为不一致
真正难的从来不是写完权限校验,而是让权限变更能秒级生效、让菜单树剪得干净又不漏项、让按钮码命名规则从第一天就没人敢破例——这些细节没压住,RBAC 就只是个听起来规范的幻觉。











