- abp版本升级8.3.0
- 数据权限增加唯一性判断
- abp8.2版本升级,blazorwasm修改为blazorapp
- 修改framework包命名规则
- (重大变更) 修改实体数据权限判断方式,无设定的用户默认无权,如需要开通所有数据权限需进行设定 参考:#30
- 升级支持Net8.0
- Abp版本升级到8.0.0
- 增加权限单元测试,参考:DemosAppServiceTests
- 增加基于Permission数据权限
//前端请参考Abp文档https://docs.abp.io/en/abp/latest/Authorization
//后端使用方法参考:AuthorizationPolicyProvider_Test
//1.在需要输出Dto上加入PermissionAttribute
public class DemoDto : FullAuditedEntityDto<Guid>, IHasConcurrencyStamp
{
public string? Name { get; set; }
[Permission(DataPermissionPermissions.Demos.Read)]//Add
public string? DisplayName { get; set; }
public RowPermissionItemDto Permission { get; set; } = new RowPermissionItemDto();
public string ConcurrencyStamp { get; set; }
}
//2.在需要控制输出的方法中使用,若无权,会替换成默认值输出
private readonly IDataPermissionAuthorizationPolicyProvider _dataPermissionAuthorizationPolicyProvider;
await _dataPermissionAuthorizationPolicyProvider.CheckListAsync(dtos);
- 增加字段级数据权限功能
- 增加实体数据权限列表,并且把行级和字段级数据权限合并在同一个明细页面 (如果希望把行级和字段级菜单单独添加可以参考DataPermissionMenuContributor自行添加)
表达式配置为”x.CreatorId=="OrganizationUser"“ 会自动寻找当前组织所有下级成员
- 支持重写获取组织成员方法(默认取abp组织)
context.Services.Replace(ServiceDescriptor.Singleton<IOrganizationStore, MyOrganizationStore>());
public class MyOrganizationStore:OrganizationStore
{
public MyOrganizationStore(IIdentityUserRepository identityUserRepository, IOrganizationUnitRepository organizationUnitRepository) : base(identityUserRepository, organizationUnitRepository)
{
}
public override async Task<List<string>> GetMenberInOrganizationUnitAsync(Guid id)
{
List<string> result = new List<string>();
result.Add(id.ToString());
return result;
}
}
- 行数据查询权限是通过仓储数据查询条件进行过滤
- 行数据权限修改和删除是通过仓储查询单个行对象再判断是否有权限,如果没有权限则抛出异常或在UI进行处理(有更好的方法也欢迎大家提出)
- 通过设置 角色 和对应实体 实现查询可以根据自定义Lambda表达式过滤查询数据
- 通过设置 角色 和对应实体 实现修改,删除,需要查询后再判断是否有权限
- 通过设置 角色 和对应实体 实现创建,目前新增需要在创建之前进行权限判断,如果没有权限则抛出异常,如: “var checkPermission = await dataPermissionStore.CheckPermissionAsync(item, PermissionType.Create);”
- 1.通过据维护的字段名称和对应角色的权限进行判断,使用字段级数据权限需要修改前端和后端代码共同实现
- 2.通过检查Permission进行判断,使用字段级数据权限需要修改前端和后端代码共同实现
- 目前缓存时间为 10 分钟,如果“PermissionExtension”对象变更时会自动清除
- 行级数据权限缓存时间为 10 分钟,权限变更后重新查询权限会进行覆盖
- 可以通过配置方式修改默认缓存时间
Configure<DataPermissionOptions>(options =>
{
options.CacheExpirationTime = 60;
});
- JS.Abp.DataPermission.Application
- JS.Abp.DataPermission.Application.Contracts
- JS.Abp.DataPermission.Domain
- JS.Abp.DataPermission.Domain.Shared
- JS.Abp.DataPermission.EntityFrameworkCore
- JS.Abp.DataPermission.HttpApi
- JS.Abp.DataPermission.HttpApi.Client
*(Optional) JS.Abp.DataPermission.Blazor *(Optional) JS.Abp.DataPermission.Blazor.Server *(Optional) JS.Abp.DataPermission.Blazor.WebAssembly *(Optional) JS.Abp.DataPermission.Web
- [DependsOn(typeof(DataPermissionApplicationModule))]
- [DependsOn(typeof(DataPermissionApplicationContractsModule))]
- [DependsOn(typeof(DataPermissionDomainModule))]
- [DependsOn(typeof(DataPermissionDomainSharedModule))]
- [DependsOn(typeof(DataPermissionEntityFrameworkCoreModule))] OR [DependsOn(typeof(DataPermissionMongoDbModule))]
- [DependsOn(typeof(DataPermissionHttpApiModule))]
- [DependsOn(typeof(DataPermissionHttpApiClientModule))]
*(Optional) [DependsOn(typeof(DataPermissionBlazorModule))] *(Optional) [DependsOn(typeof(DataPermissionBlazorServerModule))] *(Optional) [DependsOn(typeof(DataPermissionBlazorWebAssemblyModule))] *(Optional) [DependsOn(typeof(DataPermissionWebModule))]
在 YourProject.EntityFrameworkCore 项目的目录中打开命令行终端,然后键入以下命令:
> dotnet ef migrations add Added_DataPermission
> dotnet ef database update
- 以下代码已经在Demo方案实现,具体使用方法请参考Demo
- 查询表达式默认是在原有的查询表达式上进行过滤,表达式需要注意格式,如:“x.Name != "Abc"”,x.为固定格式,Name为实体属性,Abc为过滤条件,实例的含义是当前角色仅允许查询Name不等于Abc的数据
- 创建,修改和删除表达式需要注意格式,如:“x.Name == "Abc"”,x.为固定格式,Name为实体属性,Abc为过滤条件,实例的含义是当前角色仅允许创建,修改和删除Name等于Abc的数据
- 数据权限控制已提供Blazor页面,其他前端框架请自行实现
protected IDataPermissionStore dataPermissionStore => LazyServiceProvider.LazyGetRequiredService<IDataPermissionStore>();
- abp7.2 可以直接使用“query = dataPermissionStore.EntityFilter(query);//add”
protected virtual IQueryable<Demo> ApplyFilter(
IQueryable<Demo> query,
string filterText,
string name = null,
string displayName = null)
{
query = DataPermissionExtensions.EntityFilter(query, dataPermissionStore.GetAll());//add
return query
.WhereIf(!string.IsNullOrWhiteSpace(filterText), e => e.Name.Contains(filterText) || e.DisplayName.Contains(filterText))
.WhereIf(!string.IsNullOrWhiteSpace(name), e => e.Name.Contains(name))
.WhereIf(!string.IsNullOrWhiteSpace(displayName), e => e.DisplayName.Contains(displayName));
}
- 这一步是为了查询当前用户是否有该数据修改和删除权限,用于后面判断是否可以进行修改和删除操作
public async Task<Demo> GetAsync(Guid id, CancellationToken cancellationToken = default)
{
var query = await GetQueryableAsync();
query = DataPermissionExtensions.EntityFilter(query, await dataPermissionStore.GetAllAsync());//add
var item = await query.FirstOrDefaultAsync(e => e.Id == id, GetCancellationToken(cancellationToken));
await dataPermissionStore.GetPermissionAsync(id.ToString(), item);//add
return item;
}
- 在需要进行权限控制的服务引入服务“IPermissionApplicationService”
protected IPermissionApplicationService permissionApplicationService => LazyServiceProvider.LazyGetRequiredService<IPermissionApplicationService>();
- 在需要进行权限控制的方法前加入
await permissionApplicationService.CheckPermissionAsync(id.ToString(), “abp权限名称”, PermissionType.Update);
- 权限判断规则是:如果当前用户是否有“abp权限”权限,没有直接为false,再判断是否有行权限,若无须判断abp权限名称传入空值可跳过
- 在UI上控制还是在服务上进行控制可以根据实际情况进行选择,以下是以Blazor为例
var demo = await DemosAppService.GetAsync(input.Id);
CanEditDemo = PermissionApplicationService.GetAsync(demo.Id.ToString(),DataPermissionPermissions.Demos.Edit, PermissionType.Update).Result.IsGranted;//add
- 这一步是把传入的对象判断是否有权限,如果没有权限则抛出异常,需要注意是传入的对象Name必须和实体名字一致
public async Task<Demo> UpdateAsync(Guid id, UpdateDemoDto input, CancellationToken cancellationToken = default)
{
var item = await GetAsync(id, cancellationToken);
var checkPermission =await dataPermissionStore.CheckPermissionAsync(item, PermissionType.Update);//add
//Todo:无权的操作
ObjectMapper.Map(input, item);
await ValidateAsync(item);
await Repository.UpdateAsync(item, autoSave: true);
return item;
}
//add permission check
if (!dataPermissionStore.CheckPermission(demo, PermissionType.Create))
{
throw new UserFriendlyException(
"The current user does not have permission to create data!"
);
}
//创建一个数据权限的Dto
public class PermissionItemDto
{
public bool CanUpdate { get; set; }
public bool CanDelete { get; set; }
}
//在查询返回的Dto中加入
public PermissionItemDto Permission { get; set; } = new PermissionItemDto();
//AutoMapper中需要先忽略Permission
.ForMember(dest => dest.Permission, opt => opt.Ignore());
//在查询结果返回前把权限取出
var dtos = items.Select(queryResultItem =>
{
var dto = ObjectMapper.Map<Demo, DemoDto>(queryResultItem);
dto.Permission = ObjectMapper.Map<PermissionCacheItem, PermissionItemDto>(dataPermissionStore.GetPermissionAsync(queryResultItem).Result);//add
return dto;
}).ToList();
- 以下代码已经在Demo方案实现,具体使用方法请参考Demo
protected IDataPermissionItemStore dataPermissionItemStore => LazyServiceProvider.LazyGetRequiredService<IDataPermissionItemStore>();//字段级数据权限
if (dataPermissionItemStore.CheckUpdate(nameof(Demo), "Name"))
demo.Name = name;
if (dataPermissionItemStore.CheckUpdate(nameof(Demo), "DisplayName"))
demo.DisplayName = displayName;
@inject IPermissionApplicationService PermissionApplicationService
private DataPermissionItemDto DataPermissionItem { get; set; }
//这里把字段级权限获取,再传递给前端,前端根据权限判断是否显示编辑
//需要注意判断是否有权限规则如下参考:
//if (!DataPermissionItem.PermissionItems.Any(x => x.TargetType == "DisplayName")||DataPermissionItem.PermissionItems.FirstOrDefault(x => x.TargetType == "DisplayName").CanEdit)
DataPermissionItem =
await PermissionApplicationService.GetDataPermissionItemAsync(new GetPermissionItemInput()
{
ObjectName = "Demo",
});
public class DemoDto : FullAuditedEntityDto<Guid>, IHasConcurrencyStamp
{
public string? Name { get; set; }
[PermissionVerifier("Demo", "DisplayName")]//优先度高
[Permission("DataPermission.Demos")]//如果希望直接使用Abp权限,可以使用这个
public string? DisplayName { get; set; }
public RowPermissionItemDto Permission { get; set; } = new RowPermissionItemDto();
public string ConcurrencyStamp { get; set; }
}
public virtual async Task<DemoDto> GetAsync(Guid id)
{
var demo = await _demoRepository.GetAsync(id);
var item = ObjectMapper.Map<Demo, DemoDto>(demo);
await dataPermissionItemStore.CheckAsync(item);//add
//await dataPermissionItemStore.CheckListAsync(dtos);//如果是列表需要使用这个方法
item.Permission = ObjectMapper.Map<PermissionCacheItem, RowPermissionItemDto>( await dataPermissionStore.GetPermissionAsync(demo));
return item;
}
See the sample projects
账号:data001/data002 密码:1q2w3E*
感谢提供免费IDE支持此项目 (License)