








本指南详细演示如何为ABP BookStore项目的Publishers模块添加完整的权限控制,涵盖从权限定义到实际应用的全流程。

ABP的授权系统基于ASP.NET Core的授权机制,并提供了权限系统来简化权限管理。它支持:
[Authorize] 特性CrudAppService 中配置策略名称AuthorizationService 进行灵活检查为Publishers模块实现以下功能:
| 功能 | 权限名称 | 说明 |
|---|---|---|
| 查看出版商 | BookStore.Publishers |
查看列表和详情 |
| 创建出版商 | BookStore.Publishers.Create |
创建新出版商 |
| 编辑出版商 | BookStore.Publishers.Edit |
更新出版商信息 |
| 删除出版商 | BookStore.Publishers.Delete |
删除出版商 |
文件: src/Acme.BookStore.Application.Contracts/Permissions/BookStorePermissions.cs

public static class Publishers
{
public const string Default = GroupName + ".Publishers";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
命名约定:
{GroupName}.{Entity}.{Action}BookStore.Publishers.Create文件: src/Acme.BookStore.Application.Contracts/Permissions/BookStorePermissionDefinitionProvider.cs

public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(
BookStorePermissions.GroupName,
L("Permission:BookStore"));
// 添加Publishers父权限
var publishersPermission = bookStoreGroup.AddPermission(
BookStorePermissions.Publishers.Default,
L("Permission:Publishers"));
// 添加子权限
publishersPermission.AddChild(
BookStorePermissions.Publishers.Create,
L("Permission:Publishers.Create"));
publishersPermission.AddChild(
BookStorePermissions.Publishers.Edit,
L("Permission:Publishers.Edit"));
publishersPermission.AddChild(
BookStorePermissions.Publishers.Delete,
L("Permission:Publishers.Delete"));
}
权限层次结构:
BookStore (权限组)
└── Publishers (父权限)
├── Create (子权限)
├── Edit (子权限)
└── Delete (子权限)
文件: src/Acme.BookStore.Domain.Shared/Localization/BookStore/zh-Hans.json
{
"texts": {
"Permission:BookStore": "书店管理",
"Permission:Publishers": "出版商管理",
"Permission:Publishers.Create": "创建新出版商",
"Permission:Publishers.Edit": "编辑出版商",
"Permission:Publishers.Delete": "删除出版商"
}
}
适用场景:标准的 CRUD 操作
工作原理:
在构造函数中设置权限策略名称后,CrudAppService 基类会自动应用授权检查。
代码示例:
public class PublisherAppService : CrudAppService<
Publisher, PublisherDto, Guid, GetPublisherDto,
CreateUpdatePublisherDto, CreateUpdatePublisherDto>,
IPublisherAppService
{
public PublisherAppService(
IRepository<Publisher, Guid> repository,
IPublisherRepository publisherRepository,
IOptions<PublisherOptions> publisherOptions,
ILocalEventBus localEventBus)
: base(repository)
{
_publisherRepository = publisherRepository;
_publisherOptions = publisherOptions.Value;
_localEventBus = localEventBus;
// 🎯 配置权限策略 - 基类会自动使用这些配置!
GetPolicyName = BookStorePermissions.Publishers.Default;
GetListPolicyName = BookStorePermissions.Publishers.Default;
CreatePolicyName = BookStorePermissions.Publishers.Create;
UpdatePolicyName = BookStorePermissions.Publishers.Edit;
DeletePolicyName = BookStorePermissions.Publishers.Delete;
}
// 以下方法会自动检查权限(不需要手动添加 [Authorize])
public override async Task<PublisherDto> GetAsync(Guid id)
{
// 自动检查: BookStore.Publishers.Default 权限
// 如果用户没有权限,会自动返回 403 Forbidden
}
public override async Task<PagedResultDto<PublisherDto>> GetListAsync(GetPublisherDto input)
{
// 自动检查: BookStore.Publishers.Default 权限
}
public override async Task<PublisherDto> CreateAsync(CreateUpdatePublisherDto input)
{
// 自动检查: BookStore.Publishers.Create 权限
}
public override async Task<PublisherDto> UpdateAsync(Guid id, CreateUpdatePublisherDto input)
{
// 自动检查: BookStore.Publishers.Edit 权限
}
public override async Task DeleteAsync(Guid id)
{
// 自动检查: BookStore.Publishers.Delete 权限
}
}

内部机制(ABP源码简化版):
// CrudAppService 基类内部逻辑
[Authorize]
public virtual async Task<TEntityDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync(); // 检查权限
// ... 执行创建逻辑
}
protected virtual async Task CheckCreatePolicyAsync()
{
if (CreatePolicyName != null)
{
await AuthorizationService.CheckAsync(CreatePolicyName);
}
}
优点:
缺点:
CrudAppService 中使用适用场景:自定义的非 CRUD 方法
代码示例:
using Microsoft.AspNetCore.Authorization;
public class PublisherAppService : ApplicationService
{
/// <summary>
/// 批量删除出版商(需要删除权限)
/// </summary>
[Authorize(BookStorePermissions.Publishers.Delete)]
public async Task BatchDeleteAsync(List<Guid> ids)
{
foreach (var id in ids)
{
await Repository.DeleteAsync(id);
}
}
/// <summary>
/// 允许匿名访问(通常用于公开API)
/// </summary>
[AllowAnonymous]
public async Task<PublisherDto> GetPublicPublisherAsync(Guid id)
{
// 任何人都可以访问
}
/// <summary>
/// 整个服务都需要登录
/// </summary>
[Authorize]
public class PublisherAppService : ApplicationService
{
// 所有方法都需要登录
}
}
优点:
缺点:
适用场景:需要在代码中进行灵活的权限检查
using Volo.Abp.Authorization;
public class PublisherAppService : ApplicationService
{
/// <summary>
/// 获取出版商统计信息(需要查看权限)
/// </summary>
public async Task<string> GetPublisherStatisticsAsync()
{
// 检查权限,如果没有权限会抛出 AbpAuthorizationException
await AuthorizationService.CheckAsync(BookStorePermissions.Publishers.Default);
var totalCount = await Repository.CountAsync();
var recentCount = await Repository.CountAsync(
p => p.CreationTime > DateTime.Now.AddDays(-30));
return $"总出版商数: {totalCount}, 最近30天新增: {recentCount}";
}
}
特点:
AbpAuthorizationException 异常403 Forbidden 响应using Volo.Abp.Authorization;
public class PublisherAppService : ApplicationService
{
/// <summary>
/// 获取带权限信息的出版商详情
/// </summary>
public async Task<PublisherDto> GetPublisherWithDetailsAsync(Guid id)
{
var publisher = await Repository.GetAsync(id);
var dto = ObjectMapper.Map<Publisher, PublisherDto>(publisher);
// 检查用户是否有编辑权限(不会抛出异常)
bool canEdit = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Edit);
dto.CanEdit = canEdit;
// 检查用户是否有删除权限
bool canDelete = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Delete);
dto.CanDelete = canDelete;
return dto;
}
}
特点:
true 或 false前端使用示例:
// 在 Angular/React/Vue 前端
const publisher = await publisherService.getPublisherWithDetailsAsync(id);
if (publisher.canEdit) {
// 显示编辑按钮
showEditButton = true;
}
if (publisher.canDelete) {
// 显示删除按钮
showDeleteButton = true;
}
public async Task<List<string>> BatchExportAsync(List<Guid> ids)
{
var results = new List<string>();
foreach (var id in ids)
{
var publisher = await Repository.GetAsync(id);
// 检查用户是否有权限查看这个特定的出版商
if (await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Default))
{
results.Add(publisher.Name);
}
else
{
// 跳过无权限的项,而不是直接失败
results.Add($"[无权限查看] {publisher.Name}");
}
}
return results;
}
优点:
缺点:
| 使用方式 | 适用场景 | 示例方法 | 优点 | 缺点 |
|---|---|---|---|---|
| 隐式使用 | CRUD 操作 | CreateAsync, UpdateAsync, DeleteAsync, GetListAsync |
简洁,无需手动添加 | 只能在 CrudAppService 中使用,灵活性低 |
| [Authorize] 特性 | 自定义 API 方法 | BatchDeleteAsync, ExportDataAsync |
声明式,代码清晰 | 无法在方法内部添加逻辑 |
| AuthorizationService.CheckAsync | 需要强制权限检查 | GetStatisticsAsync, GetReportAsync |
灵活,可添加业务逻辑 | 需要手动编写检查代码 |
| AuthorizationService.IsGrantedAsync | 需要条件性逻辑 | GetWithPermissionsAsync, BatchExportAsync |
最灵活,可返回不同数据 | 代码量相对较多 |
BookStorePermissions.cs
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
public static class Publishers
{
public const string Default = GroupName + ".Publishers";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
BookStorePermissionDefinitionProvider.cs
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(
BookStorePermissions.GroupName,
L("Permission:BookStore"));
var publishersPermission = bookStoreGroup.AddPermission(
BookStorePermissions.Publishers.Default,
L("Permission:Publishers"));
publishersPermission.AddChild(
BookStorePermissions.Publishers.Create,
L("Permission:Publishers.Create"));
publishersPermission.AddChild(
BookStorePermissions.Publishers.Edit,
L("Permission:Publishers.Edit"));
publishersPermission.AddChild(
BookStorePermissions.Publishers.Delete,
L("Permission:Publishers.Delete"));
}
PublisherAppService.cs
using Acme.BookStore.Authors;
using Acme.BookStore.Options;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Local;
namespace Acme.BookStore.Publishers
{
public class PublisherAppService : CrudAppService<
Publisher,
PublisherDto,
Guid,
GetPublisherDto,
CreateUpdatePublisherDto,
CreateUpdatePublisherDto>,
IPublisherAppService
{
private readonly IPublisherRepository _publisherRepository;
private readonly PublisherOptions _publisherOptions;
private readonly ILocalEventBus _localEventBus;
public PublisherAppService(
IRepository<Publisher, Guid> repository,
IPublisherRepository publisherRepository,
IOptions<PublisherOptions> publisherOptions,
ILocalEventBus localEventBus)
: base(repository)
{
_publisherRepository = publisherRepository;
_publisherOptions = publisherOptions.Value;
_localEventBus = localEventBus;
// 🎯 隐式使用:配置权限策略名称
GetPolicyName = BookStorePermissions.Publishers.Default;
GetListPolicyName = BookStorePermissions.Publishers.Default;
CreatePolicyName = BookStorePermissions.Publishers.Create;
UpdatePolicyName = BookStorePermissions.Publishers.Edit;
DeletePolicyName = BookStorePermissions.Publishers.Delete;
}
// CRUD方法会自动应用权限检查(隐式方式)
public override async Task<PublisherDto> CreateAsync(CreateUpdatePublisherDto input)
{
var publisher = new Publisher(
GuidGenerator.Create(),
input.Name,
input.EstablishmentTime,
new Address(
input.Address.Street,
input.Address.City,
input.Address.StateOrProvince,
input.Address.PostalCode,
input.Address.Country
),
input.Description,
input.Url
);
await Repository.InsertAsync(publisher);
return ObjectMapper.Map<Publisher, PublisherDto>(publisher);
}
public override async Task<PagedResultDto<PublisherDto>> GetListAsync(GetPublisherDto input)
{
if (input.Sorting.IsNullOrWhiteSpace())
{
input.Sorting = _publisherOptions.DefaultSorting;
}
var maxResultCount = input.MaxResultCount > 0
? Math.Min(input.MaxResultCount, _publisherOptions.MaxPageSize)
: _publisherOptions.DefaultPageSize;
var publishers = await _publisherRepository.GetListAsync(
input.SkipCount,
maxResultCount,
input.Sorting,
input.Filter
);
var totalCount = input.Filter == null
? await _publisherRepository.CountAsync()
: await _publisherRepository.CountAsync(
publisher => publisher.Name.Contains(input.Filter));
return new PagedResultDto<PublisherDto>(
totalCount,
ObjectMapper.Map<List<Publisher>, List<PublisherDto>>(publishers)
);
}
// 🎯 显式使用1:使用 [Authorize] 特性
[Authorize(BookStorePermissions.Publishers.Delete)]
public async Task BatchDeleteAsync(List<Guid> ids)
{
foreach (var id in ids)
{
await Repository.DeleteAsync(id);
}
}
// 🎯 显式使用2:使用 AuthorizationService.CheckAsync
public async Task<string> GetPublisherStatisticsAsync()
{
await AuthorizationService.CheckAsync(BookStorePermissions.Publishers.Default);
var totalCount = await Repository.CountAsync();
var recentCount = await Repository.CountAsync(
p => p.CreationTime > DateTime.Now.AddDays(-30));
return $"总出版商数: {totalCount}, 最近30天新增: {recentCount}";
}
// 🎯 显式使用3:使用 AuthorizationService.IsGrantedAsync
public async Task<PublisherDto> GetPublisherWithDetailsAsync(Guid id)
{
var publisher = await Repository.GetAsync(id);
var dto = ObjectMapper.Map<Publisher, PublisherDto>(publisher);
dto.CanEdit = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Edit);
dto.CanDelete = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Delete);
return dto;
}
}
}
PublisherDto.cs
public class PublisherDto : FullAuditedEntityDto<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public DateTime EstablishmentTime { get; set; }
public string Address { get; set; }
public string Url { get; set; }
/// <summary>
/// 当前用户是否可以编辑此出版商
/// </summary>
public bool CanEdit { get; set; }
/// <summary>
/// 当前用户是否可以删除此出版商
/// </summary>
public bool CanDelete { get; set; }
}
IPublisherAppService.cs
public interface IPublisherAppService : IApplicationService
{
Task<PagedResultDto<PublisherDto>> GetListAsync(GetPublisherDto input);
/// <summary>
/// 批量删除出版商(需要删除权限)
/// </summary>
Task BatchDeleteAsync(List<Guid> ids);
/// <summary>
/// 获取出版商统计信息(需要查看权限)
/// </summary>
Task<string> GetPublisherStatisticsAsync();
/// <summary>
/// 获取带权限信息的出版商详情
/// </summary>
Task<PublisherDto> GetPublisherWithDetailsAsync(Guid id);
}
界面效果:
权限管理
├─ BookStore (权限组)
│ ├─ 图书管理
│ ├─ 作者管理
│ └─ 出版商管理 ← 点击展开
│ ├─ ☑ 创建新出版商
│ ├─ ☑ 编辑出版商
│ └─ ☑ 删除出版商
curl -X GET "https://localhost:44300/api/app/publisher" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# 响应:200 OK
{
"totalCount": 10,
"items": [...]
}
curl -X POST "https://localhost:44300/api/app/publisher" \
-H "Authorization: Bearer TOKEN_WITHOUT_CREATE_PERMISSION" \
-H "Content-Type: application/json" \
-d '{
"name": "示例出版社",
"establishmentTime": "2024-01-01T00:00:00Z",
"address": {
"street": "人民路123号",
"city": "北京市",
"stateOrProvince": "北京市",
"postalCode": "100000",
"country": "中国"
}
}'
# 响应:403 Forbidden
{
"error": {
"code": "Volo.Abp.Authorization.AbpAuthorizationException",
"message": "授权失败!您的权限不足:需要权限 'BookStore.Publishers.Create'",
"details": "..."
}
}
在日志中搜索权限检查相关的日志:
[INF] Authorization: Checking permission: BookStore.Publishers.Delete
[WRN] Authorization: Permission 'BookStore.Publishers.Delete' was not granted for user: john.doe
ABP 官方支持通过 multiTenancySide 参数指定权限适用范围,这对于多租户应用非常重要。
MultiTenancySides 枚举值:
Host - 仅适用于主机(租户管理员)Tenant - 仅适用于租户Both - 适用于主机和租户(默认值)代码示例:
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(
BookStorePermissions.GroupName,
L("Permission:BookStore"));
// 仅主机管理员可以访问的权限
var hostOnlyPermission = bookStoreGroup.AddPermission(
"BookStore.HostOnly",
L("Permission:HostOnly"));
hostOnlyPermission.MultiTenancySide = MultiTenancySides.Host;
// 仅租户用户可以访问的权限
var tenantOnlyPermission = bookStoreGroup.AddPermission(
"BookStore.TenantOnly",
L("Permission:TenantOnly"));
tenantOnlyPermission.MultiTenancySide = MultiTenancySides.Tenant;
// 主机和租户都可以访问的权限(默认)
var bothPermission = bookStoreGroup.AddPermission(
"BookStore.Publishers.Default",
L("Permission:Publishers"));
bothPermission.MultiTenancySide = MultiTenancySides.Both;
}
多租户场景适配:
ABP 允许通过 isEnabled: false 禁用权限,禁用后所有用户无法使用该权限,权限检查会返回 false。
禁用权限:
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(
BookStorePermissions.GroupName,
L("Permission:BookStore"));
// 禁用某个权限(例如:功能未完成时)
var disabledPermission = bookStoreGroup.AddPermission(
"BookStore.FeatureNotReady",
L("Permission:FeatureNotReady"));
disabledPermission.IsEnabled = false;
}
修改依赖模块的权限:
public override void Define(IPermissionDefinitionContext context)
{
// 获取并修改 ABP 框架内置权限
var deleteRolePermission = context.GetPermissionOrNull(IdentityPermissions.Roles.Delete);
if (deleteRolePermission != null)
{
// 禁用删除角色的权限(例如:不允许删除系统角色)
deleteRolePermission.IsEnabled = false;
}
}
适用场景:
ABP 官方提供 3 种预定义 Provider,并支持自定义 Provider 来扩展权限授予逻辑。
预定义 Provider:
UserPermissionValueProvider - 基于用户授予权限RolePermissionValueProvider - 基于角色授予权限ClientPermissionValueProvider - 基于客户端(应用)授予权限自定义 Provider 示例:
using Volo.Abp.Authorization;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
namespace Acme.BookStore.Authorization
{
public class DepartmentPermissionValueProvider : PermissionValueProvider
{
public override string Name => "Department";
protected override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{
var userId = context.Principal?.FindUserId();
if (userId == null)
{
return PermissionGrantResult.Undefined;
}
// 从用户声明中获取部门信息
var departmentClaim = context.Principal.FindFirst("Department");
if (departmentClaim == null)
{
return PermissionGrantResult.Undefined;
}
// 根据部门授予特定权限
if (departmentClaim.Value == "IT" &&
context.Permission.Name == "BookStore.TechnicalReports")
{
return PermissionGrantResult.Granted;
}
return PermissionGrantResult.Undefined;
}
}
}
注册自定义 Provider:
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpPermissionOptions>(options =>
{
options.ValueProviders.Add<DepartmentPermissionValueProvider>();
});
}
PermissionGrantResult 枚举值:
Granted - 权限已授予Denied - 权限被拒绝Undefined - 未定义(继续检查其他 Provider)ABP 支持通过实现 IAbpClaimsPrincipalContributor 接口添加自定义声明,这些声明可以用于权限检查或其他业务逻辑。
添加自定义声明示例:
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Acme.BookStore.Security
{
public class CustomClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var userId = context.ClaimsPrincipal?.FindUserId();
if (userId == null)
{
return;
}
// 从数据库获取用户额外信息
var user = await UserRepository.GetAsync(userId.Value);
// 添加自定义声明
context.Claims.Add(new Claim("Department", user.Department));
context.Claims.Add(new Claim("EmployeeId", user.EmployeeId));
context.Claims.Add(new Claim("UserType", user.Type.ToString()));
}
}
}
配置声明服务:
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpClaimsServiceOptions>(options =>
{
options.AddContributor<CustomClaimsPrincipalContributor>();
});
}
使用自定义声明:
public async Task<bool> CanAccessDepartmentReportAsync()
{
var department = CurrentUser.FindClaim("Department")?.Value;
return department == "Finance";
}
ABP 允许注册与权限名相同的 ASP.NET Core 策略,覆盖已有权限逻辑。
自定义策略示例:
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.DependencyInjection;
namespace Acme.BookStore.Authorization
{
public class TimeBasedPermissionHandler : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
// 仅在工作时间(9:00-18:00)授予权限
var now = DateTime.Now;
var isWorkingHours = now.Hour >= 9 && now.Hour < 18;
if (!isWorkingHours)
{
context.Fail("非工作时间,无法执行此操作");
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
注册自定义策略:
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddAuthorization(options =>
{
options.AddPolicy("BookStore.Publishers.Create", policy =>
{
policy.Requirements.Add(new PermissionRequirement());
policy.AddRequirements(new TimeBasedRequirement());
});
});
services.AddSingleton<IAuthorizationHandler, TimeBasedPermissionHandler>();
}
IPermissionStore 是权限数据持久化的核心接口,ABP 默认提供数据库实现(由权限管理模块提供)。
接口定义:
public interface IPermissionStore
{
Task<bool> IsGrantedAsync(string name, string providerName, string providerKey);
Task<MultiplePermissionValueProviderResult> IsGrantedAsync(string[] names);
Task<PermissionGrantResult> IsGrantedAsync(string name, string providerName, string providerKey);
}
自定义 Permission Store 示例(例如:使用 Redis 缓存):
using System.Threading.Tasks;
using Volo.Abp.Authorization;
using Volo.Abp.Caching;
using StackExchange.Redis;
namespace Acme.BookStore.Authorization
{
public class RedisPermissionStore : IPermissionStore, ITransientDependency
{
private readonly IDistributedCache<PermissionCacheItem> _cache;
private readonly IConnectionMultiplexer _redis;
public RedisPermissionStore(
IDistributedCache<PermissionCacheItem> cache,
IConnectionMultiplexer redis)
{
_cache = cache;
_redis = redis;
}
public async Task<bool> IsGrantedAsync(
string name,
string providerName,
string providerKey)
{
var cacheKey = $"Permission:{providerName}:{providerKey}:{name}";
var cached = await _cache.GetAsync(cacheKey);
return cached?.IsGranted ?? false;
}
public async Task<MultiplePermissionValueProviderResult> IsGrantedAsync(string[] names)
{
var result = new MultiplePermissionValueProviderResult();
foreach (var name in names)
{
var isGranted = await IsGrantedAsync(name, null, null);
result.Result[name] = isGranted ? PermissionGrantResult.Granted : PermissionGrantResult.Undefined;
}
return result;
}
public async Task<PermissionGrantResult> IsGrantedAsync(
string name,
string providerName,
string providerKey)
{
var isGranted = await IsGrantedAsync(name, providerName, providerKey);
return isGranted ? PermissionGrantResult.Granted : PermissionGrantResult.Undefined;
}
}
}
注册自定义 Store:
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Replace<IPermissionStore, RedisPermissionStore>();
}
ABP 支持通过 AddAlwaysAllowAuthorization() 注册服务,用于集成测试时绕过授权。
注册 AlwaysAllowAuthorizationService:
using Volo.Abp.Authorization;
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 仅在测试环境中启用
if (context.Services.IsTesting())
{
context.Services.AddAlwaysAllowAuthorization();
}
}
使用场景:
注意事项:
[AllowAnonymous] 特性ABP 支持针对具体资源(如单个出版商记录)的授权检查,通过实现 AuthorizationHandler<OperationAuthorizationRequirement, TResource>。
定义操作需求:
using Microsoft.AspNetCore.Authorization;
namespace Acme.BookStore.Authorization
{
public static class PublisherOperations
{
public static OperationAuthorizationRequirement Edit =
new OperationAuthorizationRequirement { Name = nameof(Edit) };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
}
实现授权处理器:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Authorization;
using Volo.Abp.Users;
namespace Acme.BookStore.Authorization
{
public class PublisherAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Publisher>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Publisher resource)
{
var userId = context.User.FindUserId();
if (userId == null)
{
context.Fail();
return Task.CompletedTask;
}
// 检查是否是创建者
if (resource.CreatorId == userId)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
// 检查是否有全局编辑权限
if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Publishers.Edit))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
context.Fail();
return Task.CompletedTask;
}
}
}
注册授权处理器:
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddSingleton<IAuthorizationHandler<PublisherOperations.Edit, Publisher>, PublisherAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler<PublisherOperations.Delete, Publisher>, PublisherAuthorizationHandler>();
}
使用基于资源的授权:
public async Task<PublisherDto> UpdateAsync(Guid id, CreateUpdatePublisherDto input)
{
var publisher = await Repository.GetAsync(id);
// 使用基于资源的授权检查
await AuthorizationService.CheckAsync(PublisherOperations.Edit, publisher);
// 继续更新...
}
ABP 明确前端可通过 abp.auth.isGranted("权限名") 在 JS 中检查权限。
JavaScript API 使用:
// 检查单个权限
const canCreate = abp.auth.isGranted('BookStore.Publishers.Create');
if (canCreate) {
// 显示创建按钮
$('#createButton').show();
}
// 检查多个权限
const permissions = [
'BookStore.Publishers.Create',
'BookStore.Publishers.Edit',
'BookStore.Publishers.Delete'
];
const hasAnyPermission = permissions.some(p => abp.auth.isGranted(p));
if (hasAnyPermission) {
// 显示管理面板
$('#managementPanel').show();
}
// 检查所有权限
const hasAllPermissions = permissions.every(p => abp.auth.isGranted(p));
if (hasAllPermissions) {
// 显示完整功能
$('#fullFeatures').show();
}
React 前端示例:
import { usePermissions } from '@abp/ng.core';
function PublisherList() {
const { isGranted } = usePermissions();
return (
<div>
{isGranted('BookStore.Publishers.Create') && (
<Button>Create Publisher</Button>
)}
{isGranted('BookStore.Publishers.Edit') && (
<Button>Edit</Button>
)}
{isGranted('BookStore.Publishers.Delete') && (
<Button>Delete</Button>
)}
</div>
);
}
Angular 前端示例:
import { PermissionService } from '@abp/ng.core';
@Component({
selector: 'app-publisher-list',
template: `
<button *ngIf="permissionService.isGranted('BookStore.Publishers.Create')">
Create
</button>
<button *ngIf="permissionService.isGranted('BookStore.Publishers.Edit')">
Edit
</button>
`
})
export class PublisherListComponent {
constructor(public permissionService: PermissionService) {}
}
Vue 前端示例:
import { usePermission } from '@abp/vue';
export default {
setup() {
const { isGranted } = usePermission();
return {
canCreate: computed(() => isGranted('BookStore.Publishers.Create')),
canEdit: computed(() => isGranted('BookStore.Publishers.Edit')),
canDelete: computed(() => isGranted('BookStore.Publishers.Delete'))
};
}
};
权限变化监听:
// 监听权限变化
abp.event.on('abp.permissionsChanged', () => {
console.log('Permissions changed!');
// 重新检查权限并更新 UI
updateUI();
});
A: 这些配置会被 CrudAppService 基类在内部使用。当您设置 CreatePolicyName = BookStorePermissions.Publishers.Create 后,基类会自动为 CreateAsync 方法添加授权检查。这是隐式使用,您看不到直接的调用。
验证方式:
// 您可以检查 ABP 源码中的 CrudAppService
// 它内部会调用类似这样的代码:
protected virtual async Task CheckCreatePolicyAsync()
{
if (CreatePolicyName != null)
{
await AuthorizationService.CheckAsync(CreatePolicyName);
}
}
[Authorize] 特性应该在接口定义还是实现类中添加?A: 应该在实现类中添加。
❌ 不推荐:
[Authorize(BookStorePermissions.Publishers.Create)] // 不要在接口中添加
public interface IPublisherAppService
{
Task<PublisherDto> CreateAsync(CreateUpdatePublisherDto input);
}
✅ 推荐:
public interface IPublisherAppService
{
Task<PublisherDto> CreateAsync(CreateUpdatePublisherDto input);
}
[Authorize(BookStorePermissions.Publishers.Create)] // 在实现类中添加
public class PublisherAppService : IPublisherAppService
{
// ...
}
A: 使用基于资源的授权或 AuthorizationService 检查:
public async Task<PublisherDto> UpdateAsync(Guid id, CreateUpdatePublisherDto input)
{
var publisher = await Repository.GetAsync(id);
// 方式1: 检查是否是创建者
if (publisher.CreatorId != CurrentUser.Id)
{
// 检查是否有编辑所有记录的权限
await AuthorizationService.CheckAsync(
BookStorePermissions.Publishers.Edit);
}
// 继续更新...
}
或者使用基于资源的授权(高级):
// 定义操作需求
public static class PublisherOperations
{
public static OperationAuthorizationRequirement Edit =
new OperationAuthorizationRequirement { Name = nameof(Edit) };
}
// 实现授权处理器
public class PublisherAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Publisher>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Publisher resource)
{
// 检查是否是创建者
if (resource.CreatorId == context.User.FindUserId())
{
context.Succeed(requirement);
return;
}
// 检查是否有管理权限
if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Publishers.Edit))
{
context.Succeed(requirement);
return;
}
}
}
// 在应用服务中使用
public async Task<PublisherDto> UpdateAsync(Guid id, CreateUpdatePublisherDto input)
{
var publisher = await Repository.GetAsync(id);
// 基于资源的授权检查
await AuthorizationService.CheckAsync(publisher, PublisherOperations.Edit);
// 继续更新...
}
CheckAsync 和 IsGrantedAsync 有什么区别?| 方法 | 返回值 | 无权限时的行为 | 适用场景 |
|---|---|---|---|
CheckAsync |
Task |
抛出 AbpAuthorizationException |
需要强制权限检查 |
IsGrantedAsync |
Task<bool> |
返回 false |
需要条件性逻辑 |
示例:
// CheckAsync: 无权限会抛出异常
public async Task<string> GetStatisticsAsync()
{
await AuthorizationService.CheckAsync(BookStorePermissions.Publishers.Default);
// 如果没有权限,不会执行到这里
return "...";
}
// IsGrantedAsync: 无权限返回 false
public async Task<PublisherDto> GetWithPermissionsAsync(Guid id)
{
var publisher = await Repository.GetAsync(id);
var dto = ObjectMapper.Map<Publisher, PublisherDto>(publisher);
// 不会抛出异常
dto.CanEdit = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Edit);
return dto;
}
方法1: 查看日志
[INF] Authorization: Checking permission: BookStore.Publishers.Create
[WRN] Authorization: Permission 'BookStore.Publishers.Create' was not granted for user: john.doe
方法2: 使用 Postman 测试
# 测试不同的用户/token
curl -X POST "https://localhost:44300/api/app/publisher" \
-H "Authorization: Bearer TOKEN_1"
curl -X POST "https://localhost:44300/api/app/publisher" \
-H "Authorization: Bearer TOKEN_2"
方法3: 在代码中添加日志
public async Task<PublisherDto> CreateAsync(CreateUpdatePublisherDto input)
{
Logger.LogInformation($"用户 {CurrentUser.UserName} 尝试创建出版商");
var hasPermission = await AuthorizationService.IsGrantedAsync(
BookStorePermissions.Publishers.Create);
Logger.LogInformation($"用户是否有创建权限: {hasPermission}");
// ...
}
A: 权限检查通常很快,因为:
优化建议:
// ❌ 不推荐:在循环中重复检查
public async Task ProcessPublishersAsync(List<Guid> ids)
{
foreach (var id in ids)
{
// 每次循环都检查权限(不必要)
await AuthorizationService.CheckAsync(BookStorePermissions.Publishers.Edit);
// ...
}
}
// ✅ 推荐:在循环外检查一次
public async Task ProcessPublishersAsync(List<Guid> ids)
{
// 只检查一次
await AuthorizationService.CheckAsync(BookStorePermissions.Publishers.Edit);
foreach (var id in ids)
{
// 不需要重复检查
// ...
}
}
用户请求 API 方法
↓
方法被调用
↓
┌─────────────────┐
│ 方式选择 │
└─────────────────┘
↓
┌────────┴────────┐
│ │
隐式方式 显式方式
(CrudAppService) ([Authorize] 或 AuthorizationService)
│ │
↓ ↓
检查属性值 检查特性或手动调用
│ │
└────────┬────────┘
↓
检查用户是否有权限
↓
┌─────┴─────┐
│ │
有权限 无权限
│ │
↓ ↓
执行方法 返回 403 Forbidden
│
↓
返回结果
通过本指南,您已经学会了:
关键要点:
现在您的Publishers模块已经拥有完整的权限控制!🎉
文档版本: v1.0
最后更新: 2026-01-09
适用版本: ABP v8.1
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。