文章目录
介绍
授权用于检查是否允许用户在应用程序中执行某些特定操作。
ABP通过将权限添加为自动策略来扩展ASP.NET Core授权,并允许授权系统也可在应用程序服务中使用。
因此,所有ASP.NET Core授权功能和文档在基于ABP的应用程序中均有效。本文档重点介绍在ASP.NET Core授权功能之上添加的功能。
授权属性
ASP.NET Core定义了可用于操作,控制器或页面的Authorize属性。ABP还允许您对应用程序服务使用相同的属性。
例:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
[Authorize]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
[AllowAnonymous]
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("BookStore_Author_Create")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
}
}
-
Authorize
属性强制用户登录到应用程序以使用AuthorAppService
方法。因此,GetListAsync
方法仅对经过身份验证的用户可用。 -
AllowAnonymous
禁止身份认证。因此,GetAsync
方法适用于所有人,包括未经授权的用户。 -
[Authorize("BookStore_Author_Create")]
定义一个策略(请参阅基于策略的授权),选中该策略以授权当前用户。
“BookStore_Author_Create”是任意策略名称。如果您声明这样的属性,则ASP.NET Core授权系统希望在此之前定义一个策略。
当然,您可以按照ASP.NET Core文档中所述实施策略。但是,对于简单的真/假条件(例如,是否向用户授予了策略),ABP定义了权限系统,这将在下一部分中进行说明。
权限系统
权限是为特定用户,角色或客户端授予或禁止的简单策略。
定义权限
要定义权限,请创建一个继承自PermissionDefinitionProvider
的类,如下所示:
using Volo.Abp.Authorization.Permissions;
namespace Acme.BookStore.Permissions
{
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup("BookStore");
myGroup.AddPermission("BookStore_Author_Create");
}
}
}
ABP自动发现此类。无需其他配置!
您通常在应用程序项目
Application.Contracts
中定义此类。启动模板已经附带了一个空类,名为YourProjectNamePermissionDefinitionProvider,您可以从中开始。
在Define
方法中,您首先需要添加权限组或获取现有组,然后向该组添加权限。
定义权限后,它就可以在ASP.NET Core授权系统中用作策略名称。它也将在UI中可见。查看角色的权限对话框:
-
“BookStore”组显示为左侧的新选项卡。
-
右侧的“BookStore_Author_Create”是权限名称。您可以授予或禁止该角色。
保存对话框时,该对话框将保存到数据库中并在授权系统中使用。
安装身份(identity)模块后,上面的屏幕将可用,该模块主要用于用户和角色管理。启动模板预装有身份(identity)模块。
本地化权限名称
UI的“BookStore_Author_Create”不是一个好的权限名称。幸运的是,AddPermission
和AddGroup
方法可以采用LocalizableString
作为第二个参数:
var myGroup = context.AddGroup(
"BookStore",
LocalizableString.Create<BookStoreResource>("BookStore")
);
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create")
);
然后,您可以在本地化文件中为“BookStore”和“Permission:BookStore_Author_Create”键定义文本:
"BookStore": "Book Store",
"Permission:BookStore_Author_Create": "Creating a new author"
有关更多信息,请参阅本地化系统上的本地化文档。
本地化的UI如下所示:
多租户
ABP支持多租户作为头等公民。您可以在定义新权限时定义多租户选项。它获取以下定义的三个值之一:
-
Host:权限仅适用于宿主。
-
Tenant:权限仅适用于租户。
-
Both(默认):权限仅适用于宿主和租户。
如果您的应用程序不是多租户的,则可以忽略此选项。
要设置多租户方选项,请传递给AddPermission
方法第三个参数:
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create"),
multiTenancySide: MultiTenancySides.Tenant //set multi-tenancy side!
);
启用/禁用权限
默认情况下启用权限。可以禁用权限。禁用权限将被禁止给每个人。您仍然可以检查权限,但始终会返回禁止状态。
定义示例:
myGroup.AddPermission("Author_Management", isEnabled: false);
通常,您不需要定义禁用的权限(除非您暂时想禁用应用程序的功能)。但是,您可能需要禁用在依赖模块中定义的权限。这样,您可以禁用相关的应用程序功能。有关示例用法,请参见下面的“更改从属模块的权限定义”部分。
注意:检查未定义的权限将引发异常,而禁用的权限检查仅返回禁止(false)。
子权限
权限可能具有子权限。当您要创建一个层次结构的权限树时,它特别有用,该树中的权限可能具有其他子权限,这些权限仅在已授予父权限的情况下才可用。
定义示例:
var authorManagement = myGroup.AddPermission("Author_Management");
authorManagement.AddChild("Author_Management_Create_Books");
authorManagement.AddChild("Author_Management_Edit_Books");
authorManagement.AddChild("Author_Management_Delete_Books");
UI上的结果如下所示(您可能希望本地化应用程序的权限):
对于示例代码,假定授予“Author_Management”权限的角色/用户可能具有其他权限。然后可以如下所示定义检查权限的典型应用程序服务:
[Authorize("Author_Management")]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("Author_Management_Create_Books")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Edit_Books")]
public Task UpdateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Delete_Books")]
public Task DeleteAsync(CreateAuthorDto input)
{
...
}
}
-
GetListAsync
和GetAsync
将提供给用户,如果他们有Author_Management
权限授予。 -
其他方法需要其他权限。
通过自定义策略覆盖权限
如果使用权限的相同名称定义策略并将其注册到ASP.NET Core授权系统,则您的策略将覆盖现有的权限。这是扩展对在应用程序中使用的预构建模块的授权的有效方法。
请参阅基于策略的授权文档,以了解如何定义自定义策略。
更改从属模块的权限定义
从PermissionDefinitionProvider
派生的类(就像上面的示例一样)也可以获取现有的权限定义(由依赖模块定义)并更改其定义。
例:
context
.GetPermissionOrNull(IdentityPermissions.Roles.Delete)
.IsEnabled = false;
在权限定义提供程序中编写此代码时,它将找到身份模块的“角色删除”权限并禁用了该权限,因此没有人可以在应用程序上删除角色。
提示:最好检查
GetPermissionOrNull
方法返回的值,因为如果未定义给定的权限,则该值可能返回null。
IAuthorizationService
ASP.NET Core提供了可用于检查授权的IAuthorizationService
。注入后,可以在代码中使用它来有条件地控制授权。
例:
public async Task CreateAsync(CreateAuthorDto input)
{
var result = await AuthorizationService
.AuthorizeAsync("Author_Management_Create_Books");
if (result.Succeeded == false)
{
//throw exception
throw new AbpAuthorizationException("...");
}
//continue to the normal flow...
}
当您从ABP的
ApplicationService
基类派生时,AuthorizationService
可以作为属性使用。由于它广泛用于应用程序服务中,因此为您预先注入了ApplicationService
。否则,您可以将其直接注入您的类。
由于这是一个典型的代码块,因此ABP提供了扩展方法来简化它。
例:
public async Task CreateAsync(CreateAuthorDto input)
{
await AuthorizationService.CheckAsync("Author_Management_Create_Books");
//continue to the normal flow...
}
如果未为给定权限授予当前用户/客户端,则CheckAsync
扩展方法将抛出AbpAuthorizationException
异常。还有IsGrantedAsync
扩展方法返回true
或false
。
IAuthorizationService
对于AuthorizeAsync
方法有一些重载。这些在ASP.NET Core授权文档中进行了说明。
提示:尽可能使用
Authorize
属性,因为它是声明性的且简单。使用IAuthorizationService
,如果你需要条件的检查权限,并运行基于权限检查业务代码。
在JavaScript中检查权限
您可能需要在客户端检查策略/权限。
MVC用户界面
对于ASP.NET Core MVC / Razor Pages应用程序,可以使用abp.auth
API。
示例:检查是否已为当前用户授予给定权限
abp.auth.isGranted('MyPermissionName');
Angular UI
请参阅Angular UI 的权限管理文档。
权限管理
权限管理通常由管理员用户使用权限管理模式完成:
如果需要通过代码管理权限,请注入和使用IPermissionManager
,如下所示:
public class MyService : ITransientDependency
{
private readonly IPermissionManager _permissionManager;
public MyService(IPermissionManager permissionManager)
{
_permissionManager = permissionManager;
}
public async Task GrantPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, true);
}
public async Task ProhibitPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, false);
}
}
SetForUserAsync
设置用户权限的值(true/false)。还有更多扩展方法,例如SetForRoleAsync
和SetForClientAsync
。
IPermissionManager
由权限管理模块定义。有关更多信息,请参阅权限管理模块文档。
高级主题
权限值提供程序
权限检查系统是可扩展的。从PermissionValueProvider
(或实现IPermissionValueProvider
)派生的任何类都可以参与权限检查。有三个预定义的值提供程序:
-
UserPermissionValueProvider
检查是否为当前用户授予了给定的权限。它从当前声明(claims)中获取用户id。用户声明(claims)名称是使用AbpClaimTypes.UserIdstatic
属性定义的。 -
RolePermissionValueProvider
检查是否为给定权限授予当前用户的任何角色。它从当前声明(claims)中获取角色名称。角色声明(claims)名称是使用AbpClaimTypes.Rolestatic
属性定义的。 -
ClientPermissionValueProvider
检查是否为当前客户端授予了给定的权限。这在当前没有用户的机器对机器的交互中特别有用。它从当前声明(claims)中获取客户id。客户声明(claims)名称是使用AbpClaimTypes.ClientIdstatic
属性定义的。
您可以通过定义自己的权限值提供程序来扩展权限检查系统。
例:
public class SystemAdminPermissionValueProvider : PermissionValueProvider
{
public SystemAdminPermissionValueProvider(IPermissionStore permissionStore)
: base(permissionStore)
{
}
public override string Name => "SystemAdmin";
public override async Task<PermissionGrantResult>
CheckAsync(PermissionValueCheckContext context)
{
if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
{
return PermissionGrantResult.Granted;
}
return PermissionGrantResult.Undefined;
}
}
此提供程序允许具有SystemAdmin
值的User_Type
声明(claims)的用户获得所有权限。通常IPermissionStore在权限值提供程序中使用当前声明(claims)。
权限值提供程序应从CheckAsync
方法返回以下值之一:
-
PermissionGrantResult.Granted
返回以授予用户权限。如果任何提供程序返回Granted
,则结果为Granted
,如果没有其他提供程序则返回Prohibited
。 -
PermissionGrantResult.Prohibited
返回以禁止用户获得权限。如果任何提供程序返回Prohibited
,结果将始终为Prohibited
。其他提供程序返回什么都无所谓。 -
如果此值提供程序无法决定权限值,则返回
PermissionGrantResult.Undefined
。返回此值以让其他提供者检查权限。
定义提供程序后,应将其添加到AbpPermissionOptions
,如下所示:
Configure(options =>
{
options.ValueProviders.Add();
});
C#
权限存储
IPermissionStore
是唯一需要从持久性源(通常是数据库系统)读取权限值的接口。权限管理模块将实现它并预安装在应用程序启动模板中。有关更多信息,请参阅权限管理模块文档。
AlwaysAllowAuthorizationService
AlwaysAllowAuthorizationService
是用于绕过授权服务的类。它通常用于可能要禁用授权系统的集成测试中。
使用IServiceCollection.AddAlwaysAllowAuthorization()
扩展方法来注册AlwaysAllowAuthorizationService
到依赖注入系统:
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAlwaysAllowAuthorization();
}
启动模板集成测试已完成此操作。