几乎所有的企业应用都在不同程度使用了授权。授权的目的在于检查是否允许用户在应用程序中执行特定的操作。ABP定义了一个 基于权限的 基础设施来实现授权。
授权系统使用了 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有创建和获取权限的方法。
一个权限定义了一些属性:
一个权限可以有父权限和子权限。虽然这不会影响权限检查,但是在UI上组合权限有所帮助。
当创建了授权提供者之后,我们应该在模块的PreIntialize方法中注册它:
Configuration.Authorization.Providers.Add<MyAuthorizationProvider>();
因为授权提供者会自动地注册到依赖注入系统中,所以,授权提供者通过一些其他资源,可以注入任何依赖(比如仓储)来生成权限定义。
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就不能执行此方法 }
ABP对于授权使用了强大的动态方法拦截(interception)。因此,使用AbpAuthorize特性有一些限制:
此外,
注意: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自己的实现而没有扩展任何类。
虽然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属性。这样,权限检查者不需要在应用服务类中注入就可以使用了。
视图基类定义了IsGranted方法来检查当前用户是否具有权限。因此,我们可以有条件地渲染该视图。例子:
@if (IsGranted("Administration.UserManagement.CreateUser")) { <button id="CreateNewUserButton" class="btn btn-primary"><i class="fa fa-plus"></i> @L("CreateNewUser")</button> }
在客户端,我们可以使用定义在 abp.auth 命名空间下的API。在大多数情况,我们需要检查当前的用户是否具有特定的权限(使用权限名字)。例子:
abp.auth.hasPermission('Administration.UserManagement.CreateUser');
你也可以使用 abp.auth.grantedPermissions 来获得所有授权的权限或者使用 abp.auth.allPermissions 来所有应用中可用的权限名。
我们可能需要定义权限。这时可以注入并使用 IPermissionManager 。