Skip to content
bitzorcas
中EN

Concept

Authorization 统一授权平台

从统一决策链到字段安全、共享、分权管理、权限模拟和工作流临时授权,理解当前 Authorization 平台。

Last updated

Authorization 回答的不是“用户是否已经登录”,而是:这个可信调用者,此刻能否在这个租户内,对这个资源实例执行这个动作。答案由多种策略共同给出,并且必须能被审计、失效和复现。

1. 模块到底由哪两部分组成

授权能力跨越 Framework 与 Platform 两层,不能只阅读 src/Platform/Authorization:

所有者主要类型责任
Framework ApplicationIAuthorizedRequest、AuthorizationDecisionService、各 Policy Evaluator、DataScopeResolver定义运行时决策协议和组合规则
Authorization ContractsRBAC、字段安全、共享、分权、权限申请合同与权限目录提供模块公开契约和 owner 扩展点
Authorization Application策略管理、有效权限解释、临时授权与 owner 目录校验修改策略状态并触发事件与缓存失效
Authorization Infrastructureowner-local 持久化记录、Store、缓存与双 ORM Adapter保存关系、规则、分类、Feature 覆盖和临时授权
Host 组合根CoreRuntime 与 Persistence 注册先装配占位评估器,再用生产评估器和 Store 闭合依赖

Platform 模块管理“策略事实”,Framework 执行“策略计算”。业务模块不应查询授权表,也不应复制一套 if (roles.Contains(...))。

2. 一次请求如何变成授权决策

AllowDeny

HTTP / Mediator 请求

IAuthorizedRequest
Resource + Action

AuthorizationPipelineBehavior

AuthorizationDecisionService

RBAC / AppScope

ABAC

ReBAC

Feature

Deny 优先合并

DataScope 解析

授权审计

进入 Handler

Forbidden Result
Handler 不执行

AuthorizationPipelineBehavior 位于事务行为之前。拒绝会直接返回 Authorization.Denied,Handler 不执行,事务也不应开始。这个顺序由 AuthorizationPipelineBehaviorTests.Deny_Should_Prevent_Transaction_From_Starting 固定。

3. 五类策略不是五道独立闸门

策略何时 Allow何时 Deny不适用时
RBAC非 Application 调用者拥有 {module}.{resource}.{action}不直接 DenyNeutral
AppScopeApplication 调用者拥有同名 Scope不直接 DenyNeutral
ABAC第一条命中规则的裁决为 Allow第一条命中规则为 Deny;Store 异常也 Deny无规则或无条件命中为 Neutral
ReBAC Lite用户与 Case、Client 或 Billing 实例存在允许当前动作的关系关系 Store 返回失败无主体、无实例、无关系或非目标资源为 Neutral
Featuretickets、chat、workflow、reporting 映射的 Feature 已启用映射 Feature 被禁用或 Provider 返回 false未映射模块为 Neutral

它们全部执行后再合并:任一 Deny 即拒绝;否则至少一个 Allow 才允许;全 Neutral 返回 NoMatchingPolicy。注册顺序影响第一条拒绝理由和 Allow obligations 的收集顺序,但 Feature 放在最后并不是 Deny 获胜的原因。

完整算法、缓存键和 DataScope 见 授权决策引擎。

4. 管理面与决策面必须分开理解

4.1 管理面

管理员通过 Command/Query 维护角色、用户角色、角色权限、ABAC、Feature、字段策略、数据分类、共享规则和分权授权。权限申请则由标准 Workflow 审批后产生有绝对期限的独立授权来源。写用例使用 owner-local Store,成功后发布集成事件并失效相应缓存。

4.2 决策面

业务请求携带 ResourceDescriptor 和 AuthorizationAction。评估器只读取可信主体、资源事实、规则 Store 与缓存;它不调用管理 Handler,也不把角色表直接暴露给业务模块。

一个业务命令声明自己的授权事实
public sealed record ApproveInvoiceCommand(
string InvoiceId,
decimal Amount,
string Status,
string Sensitivity) : ICommand<Result>, IAuthorizedRequest
{
// ResourceDescriptor 同时服务 RBAC 权限码、ABAC 条件和实例级 ReBAC。
public ResourceDescriptor Resource => new(
Module: "billing",
ResourceType: "invoice",
ResourceId: InvoiceId,
TenantId: TrustedTenantId,
Status: Status,
Amount: Amount,
Sensitivity: Sensitivity);
// 动作参与权限码派生,也会进入 ABAC Action 过滤和 ReBAC 动作白名单。
public AuthorizationAction Action => AuthorizationAction.Approve;
// 该值必须来自可信租户上下文,不能接受客户端请求体中的 TenantId。
private string TrustedTenantId => CurrentTenantAccessor.EffectiveTenantId;
}

上例是基于当前接口的完整声明示例,CurrentTenantAccessor 代表调用方自己的可信租户访问器。实际项目通常在构造 Command 前装入资源事实,或显式实现一个授权适配服务;不要让请求体自行声明所有者、租户或敏感级别。

5. 数据模型不是一个 Role 聚合

当前实现使用显式 Store Record 与 owner-local 关系记录,并没有文档早期所描述的 Role.Grant() 聚合:

模型稳定键与边界
RoleRecord / RoleCatalogRecord数据库 Id 用于管理;角色名是关系行的租户内稳定业务键
UserRoleRecord / UserRoleRelationRecord用户稳定键来自 Identity owner;角色 Id 或名称进入 Store 后归一化为角色名
RolePermissionRecord / RoleModulePermissionRelationRecord角色名、Menu 模块码、权限码组成关系
PermissionRecord / PermissionCatalogRecord全局权限目录;由生成或确定性种子维护
AbacRuleRecord / AbacRulePolicyRecord租户内规则;条件和裁决按整数枚举持久化
FeatureDefinitionRecord + FeatureOverrideRecord全局默认值与租户覆盖分开保存
ResourceRelationRecord租户内用户—资源关系;不建立跨模块 CLR 导航

角色生命周期、稳定键、权限授予和当前兼容性门禁见 RBAC 管理与角色生命周期。

6. HTTP 管理入口

能力代表路由资源 / 动作
角色GET/POST /api/authorization/roles、PUT/DELETE /roles/{roleId}authorization/role + View/Create/Update/Delete
用户角色GET /users/{userId}/roles、POST/DELETE /users/{userId}/roles/{roleId}authorization/user-role + View/Assign/Revoke
角色权限GET/POST /roles/{roleId}/permissions、DELETE /roles/{roleId}/permissions/{permissionId}authorization/role-permission + View/Grant/Revoke
权限树GET /api/authorization/permissions/treeauthorization/permission + View
ABACGET/POST /api/authorization/abac-rules、PUT/DELETE /abac-rules/{ruleId}authorization/abac-rule + View/Create/Update/Delete
FeatureGET /api/authorization/features、PUT /features/{featureCode}authorization/feature + View/Update
高级治理/field-security、/sharing、/delegated-admin、/permission-audit、/permission-simulation、/permission-requests各能力独立 View/Manage/Approve/Reject/Revoke 权限

当前 Grant/Revoke 命令已分别使用 AuthorizationAction.Grant 与 AuthorizationAction.Revoke,与公开权限目录一致。用户角色分配和撤销也按命令中的目标主体失效权限与菜单缓存,不再失效操作者。

7. 缓存与事件的真实边界

  • 角色创建、更新、删除与角色权限变化发布事件,并按租户失效 Permission 决策缓存。
  • ABAC 创建、更新、删除按有效租户失效 Permission 决策缓存。
  • Feature 更新区分全局默认与租户覆盖,发布 FeatureChangedIntegrationEvent;全局默认变更失效全部 Feature 缓存,租户覆盖只失效目标租户。
  • 决策缓存键覆盖调用者、角色、权限、租户、客户端和全部资源事实;Delegated 调用者绕过固定 TTL 决策缓存。
  • DataScope 和 ReBAC 有独立缓存端口,它们不能依赖 Permission 缓存失效“顺便清掉”。

角色变更通过 AuthorizationSubjectCacheInvalidation 同时处理目标主体权限与跨实例菜单同步。UserGroup 关系、字段策略、共享规则和临时授权仍有各自的事实源和失效路径;不要假设清除 Permission Cache 会顺带更新所有派生目录。详见配置、持久化与缓存。

8. 章节地图

想解决的问题阅读入口
理解决策组合、缓存、DataScope 与审计授权决策引擎
设计角色、授予权限和用户分配流程RBAC 管理与角色生命周期
使用金额、状态、关系与 Feature 权益ABAC、ReBAC 与 Feature
装配生产 Store、理解表与失效范围配置、持久化与缓存
接入字段安全、共享、分权和临时授权高级治理、测试与运维

9. 源码审查入口

在 BitzOrcasVNext 根目录运行:

Terminal window
# 同时查看 Framework 决策核心与 Platform 策略管理,避免只读半个模块。
rg -n "class AuthorizationDecisionService|class .*PolicyEvaluator|class .*Role" \
src/Framework/BitzOrcas.Application/Authorization src/Platform/Authorization -g '*.cs'
# 对比命令动作与权限目录,Grant/Revoke 应保持同构。
rg -n "AuthorizationAction\.(Grant|Revoke)|role-permission\.(grant|revoke)|user-role\.revoke" \
src/Platform/Authorization -g '*.cs'
# 查看所有缓存失效调用;目标用户、租户、资源和 Feature 标签必须与变更对象一致。
rg -n "InvalidateBy(User|Tenant|Resource)Async" \
src/Platform/Authorization src/Framework/BitzOrcas.Application/Authorization -g '*.cs'

10. 商业交付最低证据

授权模块进入 GA 前至少需要证明:

  1. 权限目录和所有 IAuthorizedRequest 派生权限码完全一致;
  2. 分配或撤销角色后,目标用户的旧 Allow、旧 Deny 和菜单投影都立即失效;
  3. ABAC、ReBAC 和 Feature 的存储故障路径保持关闭式拒绝;
  4. DataScope 被调用方真正转换为查询过滤器,并有跨租户集成测试;
  5. 缓存命中仍重算当前 DataScope、写本次审计,Delegated 身份不跨有效期缓存;
  6. 字段安全、共享、分权与临时授权的 owner 执行面没有旁路;
  7. 双 ORM Store 契约、种子幂等、全局权限与 Feature 唯一性测试均通过。

下一篇:授权决策引擎 · 返回平台模块目录

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%