在业务系统需求规划过程中,通常对于诸如组织机构、用户和角色等这种基础功能,通常是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来讲,是系统的基石,整个业务体系是建立于这部分基石之上的,当然,还有诸如多语言、设置管理、认证和授权等。对于这部分功能,ABP中存在这些概念,并且通过Module Zero模块完成了这些概念。

一、角色访问控制之RBAC

  RBAC:Role Based Access Control,基于角色的访问控制,这在目前大多数软件中来讲已经算得上是普遍应用了,最常见的结构如下,结构简单,设计思路清晰。

  

  但是也存在其它升级版的设计,诸如用户权限表、角色组、用户组的概念等,具体分类有RBAC0、RBAC1、RBAC2等,后者功能越来越强大,也越来越复杂。

  • RBAC0:是RBAC的核心思想。
  • RBAC1:是把RBAC的角色分层模型。
  • RBAC2:增加了RBAC的约束模型。
  • RBAC3:整合RBAC2 + RBAC1。

二、ABP中的RBAC

  在Abp中,已经集成了这些概念,并在ModuleZero模块中实现了这些概念,基于IdentityServer4的ModuleZero模块完成了封装。对于我们大多数以业务为中心的开发人员来讲,不应该又去造一个轮子,而是应该开好这辆车。首先看下Abp中的RBAC模型

  

  在这其中权限表中记录了用户与权限,角色与权限两部分。对于权限通常指的是功能权限和数据权限两部分,一般来讲,大多指的是功能权限,这种通过角色与权限进行管理即可,如还有用户部分的功能区分,则可以再使用上用户与权限,而对于数据权限,可以利用用户与权限部分,个人用的比较少,但是,可以想象到这么一个场景,针对于一家门店内的多个店长,角色相同即相应的权限相同,但各自关心的数据来源不同,关心东部、南部等数据,而不关心西部、北部数据,因此可以在数据层面进行划分,比如设置数据来源,东南西北,对于数据来源进行权限关联,这样一来用户本身如果拥有东部数据权限,则只能看到东部数据。

1、权限声明及应用

  在Abp中,需要首先在Core层/Authorization/PermissionNames.cs中声明权限,Abp权限部分设计原则是:先声明再使用

/// <summary>
/// 权限命名
/// </summary>
public static class PermissionNames
{
#region 顶级权限
public const string Pages = "Pages";
#endregion #region 基础支撑平台
public const string Pages_Frame = "Pages.Frame"; #region 租户管理
public const string Pages_Frame_Tenants = "Pages.Frame.Tenants";
#endregion #region 组织机构
public const string Pages_Frame_OrganizationUnits = "Pages.Frame.OrganizationUnits";
public const string Pages_Frame_OrganizationUnits_Create = "Pages.Frame.OrganizationUnits.Create";
public const string Pages_Frame_OrganizationUnits_Update = "Pages.Frame.OrganizationUnits.Update";
public const string Pages_Frame_OrganizationUnits_Delete = "Pages.Frame.OrganizationUnits.Delete";
#endregion #region 用户管理
public const string Pages_Frame_Users = "Pages.Frame.Users";
public const string Pages_Frame_Users_Create = "Pages.Frame.Users.Create";
public const string Pages_Frame_Users_Update = "Pages.Frame.Users.Update";
public const string Pages_Frame_Users_Delete = "Pages.Frame.Users.Delete";
public const string Pages_Frame_Users_ResetPassword = "Pages.Frame.Users.ResetPassword";
#endregion #region 角色管理
public const string Pages_Frame_Roles = "Pages.Roles";
public const string Pages_Frame_Roles_Create = "Pages.Frame.Roles.Create";
public const string Pages_Frame_Roles_Update = "Pages.Frame.Roles.Update";
public const string Pages_Frame_Roles_Delete = "Pages.Frame.Roles.Delete";
#endregion }

  然后在Core层/Authorization/XXXAuthorizationProvider.cs中设置具体权限,在此处设置权限时,可以根据权限设计时候的职责划分,比如如果仅仅是多租户需要这部分,那便设置权限范围为多租户即可。

public class SurroundAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
#region 顶级权限
var pages = context.CreatePermission(PermissionNames.Pages, L("Pages"));
#endregion #region 基础支撑平台
var frame = pages.CreateChildPermission(PermissionNames.Pages_Frame, L("Frame")); #region 租户管理
frame.CreateChildPermission(PermissionNames.Pages_Frame_Tenants, L("Tenants"), multiTenancySides: MultiTenancySides.Host);
#endregion #region 组织机构
var organizationUnits = frame.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits, L("OrganizationUnits"));
organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Create, L("CreateOrganizationUnit"));
organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Update, L("EditOrganizationUnit"));
organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Delete, L("DeleteOrganizationUnit"));
#endregion #region 用户管理
var users = frame.CreateChildPermission(PermissionNames.Pages_Frame_Users, L("Users"));
users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Create, L("CreateUser"));
users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Update, L("UpdateUser"));
users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Delete, L("DeleteUser"));
users.CreateChildPermission(PermissionNames.Pages_Frame_Users_ResetPassword, L("ResetPassword"));
#endregion #region 角色管理
var roles = frame.CreateChildPermission(PermissionNames.Pages_Frame_Roles, L("Roles"));
roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Create, L("CreateRole"));
roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Update, L("UpdateRole"));
roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Delete, L("DeleteRole"));
#endregion
}
}

  在设置完毕后,需要将该类集成到Core层/XXXCoreModule当前模块中,才能使得该部分权限设置生效。

//配置权限管理
Configuration.Authorization.Providers.Add<SurroundAuthorizationProvider>();

  作为业务的入口,菜单是较为直观的体现方式,现在可以,为菜单分配权限了,拥有权限的人才能看的到菜单,同时后台方法中也要有权限判定,菜单仅作为前端入口上的控制,权限判定作为后端的控制。在MVC层的Startup/XXXNavigationProvider.cs中完成菜单的配置工作,可以配置多级菜单,每个菜单可以配置相应的权限,在生成菜单判定时,如果父级菜单权限不足,则直接会跳过子级菜单的判定。

new MenuItemDefinition(//基础支撑
PageNames.FrameManage,
L(PageNames.FrameManage),
icon: "",
requiredPermissionName: PermissionNames.Pages_Frame
).AddItem(
new MenuItemDefinition(//组织机构
PageNames.OrganizationUnits,
L(PageNames.OrganizationUnits),
url: "/OrganizationUnits",
icon: "",
requiredPermissionName: PermissionNames.Pages_Frame_OrganizationUnits
)
).AddItem(
new MenuItemDefinition(//用户管理
PageNames.Users,
L(PageNames.Users),
url: "/Users",
icon: "",
requiredPermissionName: PermissionNames.Pages_Frame_Users
)
).AddItem(
new MenuItemDefinition(//角色管理
PageNames.Roles,
L(PageNames.Roles),
url: "/Roles",
icon: "",
requiredPermissionName: PermissionNames.Pages_Frame_Roles
)
).AddItem(
new MenuItemDefinition(//系统设置
PageNames.HostSettings,
L(PageNames.HostSettings),
url: "/HostSettings",
icon: "",
requiredPermissionName: PermissionNames.Pages_Frame_HostSettings
)
)

  在前端页面上,对于按钮级别的控制也通过权限判定,Abp提供了判定方法,利用Razor语法进行按钮控制

@if (await PermissionChecker.IsGrantedAsync(PermissionNames.Pages_Core_DataDictionary_Create))
{
<button class="layui-btn layuiadmin-btn-dataDictionary" data-type="addDataDictionary">添加类型</button>
}

  在后端方法上,通常我喜欢直接在应用服务中的方法上做权限判定(当然也可以前移到MVC层,但是这样一来,针对于WebApi形式的Host层,又得多加一次判定了),利用AbpAuthorize特性,判定该方法需要哪几个权限才能访问,而在mvc的控制器上做访问认证。

[AbpAuthorize(PermissionNames.Pages_Core_DataDictionary_Create)]
private async Task CreateDataDictionaryAsync(CreateOrUpdateDataDictionaryInput input)
{ }

2、角色与权限

  在Abp中,角色信息存储在abprole表中,角色与权限间的关联存储在abppermission这张表中,一个角色有多个权限,如果某个角色的权限被去掉了,这张表中的相关记录将由abp负责删除,我们只需要完成掌控哪些权限是这个角色有的就行。Abp中已经完成了角色的所有操作,但是前端部分采用的是bootstrap弄的,将其改造一波,成为layui风格。

  

  在创建角色中,主要是将选中的权限挂钩到具体的某个角色上,该部分代码沿用abp中自带的角色权限处理方法。

private async Task CreateRole(CreateOrUpdateRoleInput input)
{
var role = ObjectMapper.Map<Role>(input.Role);
role.SetNormalizedName(); CheckErrors(await _roleManager.CreateAsync(role)); var grantedPermissions = PermissionManager
.GetAllPermissions()
.Where(p => input.PermissionNames.Contains(p.Name))
.ToList(); await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);
}

  指定角色Id,租户Id及之前声明的权限名称,在abppermission中可查看到具体角色权限。

  

3、用户与角色

  一个用户可以承担多个角色,履行不同角色的义务,作为一个业务系统最基本的单元,abp中提供了这些概念并在Module Zero模块中已经完成了对用户的一系列操作,用户信息存储在AbpUsers表中,用户直接关联的角色保存在AbpUserRoles表中,abp中MVC版本采用的是bootstrap风格,因此,用layui风格完成一次替换,并且,改动一些页面布局。

  

  Abp版本中,由于是土耳其大佬所开发的习惯,针对于姓和名做了拆分,因此对于我们的使用要做一次处理,我这先简单处理了一下,并且在业务系统中,邮箱时有时无,因此也需要进行考虑。

[AbpAuthorize(PermissionNames.Pages_Frame_Users_Create)]
private async Task CreateUser(CreateOrUpdateUserInput input)
{
var user = ObjectMapper.Map<User>(input.User);
user.TenantId = AbpSession.TenantId;
user.IsEmailConfirmed = true;
user.Name = "Name";
user.Surname = "Surname";
//user.EmailAddress = string.Empty; await UserManager.InitializeOptionsAsync(AbpSession.TenantId);
foreach (var validator in _passwordValidators)
{
CheckErrors(await validator.ValidateAsync(UserManager, user, AppConsts.DefaultPassword));
} user.Password = _passwordHasher.HashPassword(user, AppConsts.DefaultPassword); await _userManager.InitializeOptionsAsync(AbpSession.TenantId); CheckErrors(await _userManager.CreateAsync(user, AppConsts.DefaultPassword)); if (input.AssignedRoleNames != null)
{
CheckErrors(await _userManager.SetRoles(user, input.AssignedRoleNames));
} if (input.OrganizationUnitIds != null)
{
await _userManager.SetOrganizationUnitsAsync(user, input.OrganizationUnitIds);
} CurrentUnitOfWork.SaveChanges();
}

  此处对用户个人单独的权限没有去做处理,依照Abp的文档有那么一句话,大多数应用程序中,基于角色的已经足够使用了,如果想声明特定权限给用户,那么针对于用户本身的角色权限则被覆盖。    

 

 至此,修改整合用户、角色和权限加入到系统中初步完成了,至于一些更为丰富的功能,待逐步加入中,车子再好,司机也得睡觉。

 仓库地址:https://gitee.com/530521314/Partner.Surround.git

2019-11-17,望技术有成后能回来看见自己的脚步

X-Admin&ABP框架开发-RBAC的更多相关文章

  1. X-Admin&ABP框架开发-消息通知

    业务型网站使用过程中,消息通知是一个不可或缺的功能,采用站内通知.短信通知.邮件通知.微信通知等等各种方式都有,ABP框架对这部分工作已经封装的很好了,站在巨人的肩膀上,一览全貌,带来的就是心情舒畅. ...

  2. X-Admin&ABP框架开发-系统日志

    网站正常运行中有时出现异常在所难免,查看系统运行日志分析问题并能够根据错误信息快速解决问题尤为重要,ABP对于系统运行日志这块已经做了很好的处理,默认采用的Log4Net已经足够满足开发过程中的需要了 ...

  3. X-Admin&ABP框架开发-版本管理

    多租户系统中,针对于不同租户开放不同功能,或是按照不同功能进行收费管理,需要从宿主本身去管理租户的版本信息,如同酒店人员对不同房间收取不同费用,依据房间内部设施,房间大小等设置不同收费标准.Abp系统 ...

  4. X-Admin&ABP框架开发-代码生成器

    在日常开发中,有时会遇到一些相似的代码,甚至是只要CV一次,改几个名称,就可以实现功能了,而且总归起来,都可以由一些公用的页面更改而来,因此,结合我日常开发中使用到的页面,封装一个适合自己的代码生成器 ...

  5. X-Admin&ABP框架开发-设置管理

    在网站开发中,设置是不可缺少的一环,如用户设置.系统设置.甚至是租户设置等.ABP对于设置的管理已经做了很好的处理,我们可以借助巨人的力量来完成我们的冒险. ABP官网地址:https://aspne ...

  6. X-Admin&ABP框架开发-数据字典

    在业务型的系统开发中,我们需要维护各种个样的类型,比如客户类型.客户行业.商品类型等等,这些类型往往信息量不多,并且相似度极高,如果采用一类型一表去设计,将会造成极大的工作量,通过将这部分类型的信息进 ...

  7. X-Admin&ABP框架开发-租户管理

    软件即服务概念的推动,定制化到通用化的发展,用一套代码完成适应不同企业的需求,利用多租户技术可以去做到这一点.ABP里提供了多租户这一概念并且也在Zero模块中实现了这一概念. 一.多租户的概念 单部 ...

  8. 高薪诚聘熟悉ABP框架的.NET高级开发工程师(2016年7月28日重发)

    招聘单位是ABP架构设计交流群(134710707)群主阳铭所在的公司-上海运图贸易有限公司 招聘岗位:.NET高级开发工程师工作地点:上海-普陀区 [公司情况]上海运图贸易有限公司,是由易迅网的创始 ...

  9. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

随机推荐

  1. Java学习笔记之基础语法(数组)

    数组 数组概述:是具有相同数据类型的数据的集合 数组的定义:数据类型 数组名 [] 数组特点: 1,数组是引用数据类型. 2,数组值用大括号,元素之间用逗号隔开,元素的个数是0-N个 3,数组长度是固 ...

  2. 基于常规DNS隧道进行的tcp端口转发dns2tcp的使用

    0x01 安装Dns2TCP dns2tcp 是一个利用DNS隧道转发TCP连接的工具,使用C语言开发. sudo apt-get install dns2tcp 0x02配置dns2tcp 配置DN ...

  3. SQLMAP SSI注入错误解决

    记一次SQL注入 目标地址:https://www.xxxx.com/ 之前补天提交过这个注入  后来貌似”修复了”(实际就是装了安全狗和过滤了一些关键字) 不过今天试了下 还是可以注入 可以看到已经 ...

  4. vi/vim编辑器使用方法详解

    vi编辑器是所有Unix及Linux系统下标准的编辑器,他就相当于windows系统中的记事本一样,它的强大不逊色于任何最新的文本编辑器.他是我们使用Linux系统不能缺少的工具.由于对Unix及li ...

  5. 项目代码管理工具Git的总结

    在项目的开发中,代码的同步管理很重要,团队的几个人可以通过免费的github管理自己的开源项目代码,高效方便.下面说说,开发中经常用到的git指令操作,基于github平台. 0.配置提交者的账户和邮 ...

  6. JAVA动态代理 你真的完全了解Java动态代理吗?

    网上讲JAVA动态代理,说的天花乱坠,发现一篇文章写的通俗易懂,特意转载过来 原文地址:https://www.jianshu.com/p/95970b089360 动态代理看起来好像是个什么高大上的 ...

  7. react - 组件间的值传递

    父组件向子组件传值 父组件通过属性进行传递,子组件通过props获取 //父组件 class CommentList extends Component{ render(){ return( < ...

  8. 数据结构(四十)平衡二叉树(AVL树)

    一.平衡二叉树的定义 平衡二叉树(Self-Balancing Binary Search Tree或Height-Balanced Binary Search Tree),是一种二叉排序树,其中每一 ...

  9. unity 初始化数据存储问题

    在用unity进行开发的时初始化的数据和中间实时生成的数据存储不同,初始化文件数据建议安放在asset-StreamingAssets文件下,需要时读取取来.运行时所需的实时文件或数据持久化的xml文 ...

  10. 第二篇:版本控制git之分支

    有人把 Git 的分支模型称为它的`‘必杀技特性’',也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出. 为何 Git 的分支模型如此出众呢? Git 处理分支的方式可谓是难以置信的轻量, ...