本篇目录
介绍###
几乎所有的企业应用都在不同程度使用了授权。授权的目的在于检查是否允许用户在应用程序中执行特定的操作。ABP定义了一个基于权限的基础设施来实现授权。
关于IPermissionChecker
授权系统使用了IPermissionChecker来检查权限。虽然你可以用自己的方式实现该接口,但是它已完全实现在了 module-zero项目中。如果没有实现该接口,那么系统会默认使用NullPermissionChecker将所有的权限授予给每个人。
定义权限###
一个唯一的权限是为需要授权的每个操作定义的。我们应该在使用权限之前定义一个权限。ABP的设计是模块化的,因此不同的模块可以有不同的权限。为了定义模块的权限,应该创建一个派生自AuthorizationProvider(以下翻译为授权提供者)的类。一个授权提供者的例子如下所示:
public class MyAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
var administration = context.CreatePermission("Administration");
var userManagement = administration.CreateChildPermission("Administration.UserManagement");
userManagement.CreateChildPermission("Administration.UserManagement.CreateUser");
var roleManagement = administration.CreateChildPermission("Administration.RoleManagement");
}
}
IPermissionDefinitionContext有创建和获取权限的方法。
一个权限定义了一些属性:
- Name:系统中 唯一的名字。最好为权限的名字定义一个const字符串而不是变量字符串。我们偏向使用“.”符号用于有层次的名字,但这不是强制的。你可以设置任何你喜欢的名字,唯一的一点是保证它必须是唯一的。
- DisplayName:用于以后在UI上显示权限的本地化字符串。
- Description:用于以后在UI上显示权限定义的本地化字符串。
- IsGrantedByDefault:表示该权限是否授予给所有登录的用户,除非该权限显式禁止未授予给用户。该值一般默认为false。
- MultiTenancySides:对于多租户应用,租户或者租主可以使用同一个权限。这是一个Flags枚举,因此一个权限可以用于租户和租主。
- dependedFeature:可以用于声明一个功能的依赖。因此,只有功能依赖满足了,该权限才会被授予。
一个权限可以有父权限和子权限。虽然这不会影响权限检查,但是在UI上组合权限有所帮助。
当创建了授权提供者之后,我们应该在模块的PreIntialize方法中注册它:
Configuration.Authorization.Providers.Add<MyAuthorizationProvider>();
因为授权提供者会自动地注册到依赖注入系统中,所以,授权提供者通过一些其他资源,可以注入任何依赖(比如仓储)来生成权限定义。
检查权限###
使用AbpAuthorize特性
AbpAuthorize(MVC控制器是AbpMvcAuthorize,Web API控制器是AbpApiAuthorize)是最简单也是最普通的检查权限的方式。思考一下下面的应用服务方法:
[AbpAuthorize("Administration.UserManagement.CreateUser")]
public void CreateUser(CreateUserInput input)
{
//如果一个用户没有被授予 "Administration.UserManagement.CreateUser" 权限,那么ta就不能执行此方法
}
AbpAuthorize特性也会检查当前的用户是否已经登录(使用IAbpSession.UserId)。因此,如果我们为一个方法声明了AbpAuthorize,它至少会检查登录情况:
[AbpAuthorize]
public void SomeMethod(SomeMethodInput input)
{
//如果用户没有登录,那么ta就不能执行此方法
}
AbpAuthorize特性需要注意的地方
ABP对于授权使用了强大的动态方法拦截(interception)。因此,使用AbpAuthorize特性有一些限制:
- 不能用于私有方法。
- 不能用于静态方法。
- 不能用于非注入类的方法(我们必须要使用依赖注入)。
此外,
- 可以用于任何 public方法,如果该方法是通过接口调用的(比如应用服务通过接口使用)。
- 方法应该是virtual的,如果它是从类的引用直接调用的(比如ASP.Net MVC或者Web API的控制器)。
- 如果方法是protected的,那么它应该是 virtual的。
注意:AbpAuthorize特性有三个:
在应用服务中(应用层),我们使用Abp.Authorization.AbpAuthorize类。
在MVC控制器中(Web层),我们使用 Abp.Web.Mvc.Authorization.AbpMvcAuthorize类。
在ASP.NET Web API中,我们使用 Abp.WebApi.Authorization.AbpApiAuthorize特性。
这写特性的差异来自继承。在MVC端,派生自MVC自己的Authorize类。在Web API端,它派生自Web API的Authorize类。因此,它已经很好地集成到了MVC和Web API。但是在应用层,它完全是ABP自己的实现而没有扩展任何类。
使用IPermissionChecker
虽然AbpAuthorize特性对于大多数情况相当够用了,但是肯定存在我们会在一个方法体内检查权限的情况。我们可以注入并使用IPermissionChecker,如下面的例子所示:
public void CreateUser(CreateOrUpdateUserInput input)
{
if (!PermissionChecker.IsGranted("Administration.UserManagement.CreateUser"))
{
throw new AbpAuthorizationException("You are not authorized to create user!");
}
//如果一个用户没有"Administration.UserManagement.CreateUser" 权限,那么ta不能到达该点。
}
当然,你可以编写任何逻辑代码,因为IsGranted仅仅返回true或者false(也有Async版本)。如果你只是检查一个权限然后抛出一个如上所示的异常,那么你可以使用 Authorize方法:
public void CreateUser(CreateOrUpdateUserInput input)
{
PermissionChecker.Authorize("Administration.UserManagement.CreateUser");
//如果一个用户没有"Administration.UserManagement.CreateUser" 权限,那么ta不能到达该点。
}
因为授权一般在应用层实现,所以ApplicationService基类注入并定义了PermissionChecker属性。这样,权限检查者不需要在应用服务类中注入就可以使用了。
Razor视图
视图基类定义了IsGranted方法来检查当前用户是否具有权限。因此,我们可以有条件地渲染该视图。例子:
@if (IsGranted("Administration.UserManagement.CreateUser"))
{
<button id="CreateNewUserButton" class="btn btn-primary"><i class="fa fa-plus"></i> @L("CreateNewUser")</button>
}
客户端(Javascript)
在客户端,我们可以使用定义在abp.auth命名空间下的API。在大多数情况,我们需要检查当前的用户是否具有特定的权限(使用权限名字)。例子:
abp.auth.hasPermission('Administration.UserManagement.CreateUser');
你也可以使用abp.auth.grantedPermissions来获得所有授权的权限或者使用 abp.auth.allPermissions来获取所有应用中可用的权限名。
注意:自ABP 0.7.8版本开始,将javascript端的abp.auth.hasPermission更名为abp.auth.isGranted。hasPermission已经过时了。在新的项目中不要使用abp.auth.hasPermission。
权限管理者###
我们可能需要定义权限。这时可以注入并使用IPermissionManager。