ABP理论学习之授权(Authorization)

返回总目录


本篇目录

介绍

几乎所有的企业应用都在不同程度使用了授权。授权的目的在于检查是否允许用户在应用程序中执行特定的操作。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

posted @ 2016-01-02 11:32 tkbSimplest 阅读(...) 评论(...) 编辑 收藏