授权系统的三层结构
ABP 的权限不是扁平的字符串列表,而是三层结构:
// PermissionGroupDefinition.cs — 组,功能模块的顶级分类
context.AddGroup("BookStore", L("图书管理"));
// PermissionDefinition.cs — 权限,具体功能
group.AddPermission("BookStore.Books", L("图书管理"));
// 子权限(Children)— 更细粒度的操作
books.AddChild("BookStore.Books.Create", L("创建"));
books.AddChild("BookStore.Books.Delete", L("删除"));PermissionDefinition 的两个关键属性:
public class PermissionDefinition
{
public string Name { get; } // 权限唯一标识,如 "BookStore.Books"
public PermissionDefinition? Parent { get; private set; } // 父权限
public IReadOnlyList<PermissionDefinition> Children { get; } // 子权限
// 子权限继承:如果用户没有 "BookStore.Books" 权限,
// 即使勾选了 "BookStore.Books.Create" 也不应该生效
}权限定义器的工作流程
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency
{
public virtual void PreDefine(IPermissionDefinitionContext context) { }
public abstract void Define(IPermissionDefinitionContext context); // 必须实现
public virtual void PostDefine(IPermissionDefinitionContext context) { }
}三个方法按 PreDefine → Define → PostDefine 顺序调用,类似模块生命周期的三段式。PreDefine 和 PostDefine 用于模块间的权限定义覆盖。
权限检查链
当调用 [Authorize("BookStore.Books.Create")] 时,完整链如下:
1. ASP.NET Core AuthorizeFilter(MVC 过滤器)
↓ 检查用户是否已认证
2. Castle DynamicProxy AbpAuthorizationInterceptor
↓ 检查 [Authorize] 属性
3. IAuthorizationService.CheckAsync("BookStore.Books.Create")
↓
4. IPermissionChecker.IsGrantedAsync("BookStore.Books.Create")
↓
5. PermissionStore 从数据库查询用户/角色是否有该权限
↓
6. 返回 true → 继续执行
返回 false → 抛出 AbpAuthorizationException → HTTP 403IPermissionChecker 的层次
public interface IPermissionChecker
{
// 检查单个权限
Task<bool> IsGrantedAsync(string name);
// 批量检查多个权限(一次数据库查询,减少 N+1)
Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names);
}IsGrantedAsync(string[]) 用于页面渲染时批量检查权限,避免多次数据库查询。
ICurrentUser 和 IAuthorizationService 的职责分离
// ICurrentUser — 提供用户身份信息(适合 UI 层判断"当前是谁")
public interface ICurrentUser
{
bool IsAuthenticated { get; }
Guid? Id { get; }
string[] Roles { get; }
bool IsInRole(string roleName);
}
// IAuthorizationService — 提供权限检查(适合业务层"能做什么")
// 通过 ApplicationService 基类可直接使用
await AuthorizationService.CheckAsync("BookStore.Books");
await AuthorizationService.IsGrantedAsync("BookStore.Books");实战:三种授权方式的选用
// 场景 1:接口级别的权限控制 → [Authorize] 最合适
[Authorize("BookStore.Books.Create")]
public async Task<BookDto> CreateAsync(CreateBookDto input) { }
// 场景 2:方法内部条件性判断 → AuthorizationService
public async Task DeleteAsync(Guid id)
{
if (CurrentUser.Id == book.CreatorId)
{
// 创建者可以删除自己的书
await _bookRepo.DeleteAsync(id);
}
else if (await AuthorizationService.IsGrantedAsync("BookStore.Books.Delete"))
{
// 有权限的管理员也可以删除
await _bookRepo.DeleteAsync(id);
}
}
// 场景 3:只在 UI 上隐藏按钮 → ICurrentUser 或直接查权限
if (await AuthorizationService.IsGrantedAsync("BookStore.Books.Delete"))
{
// 显示"删除"按钮
}
夜雨聆风