开发者社区> 陈珙> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

.Net Core实战之基于角色的访问控制的设计(一)

简介: .Net Core实战之基于角色的访问控制的设计(一)
+关注继续查看
福利推荐:阿里云、腾讯云、华为云等大品牌云产品全线2折优惠活动来袭,4核8G云服务器899元/3年,新老用户共享优惠,点击这里立即抢购>>>

前言

  

上个月,我写了两篇微服务的文章:《.Net微服务实战之技术架构分层篇》与《.Net微服务实战之技术选型篇》,微服务系列原有三篇,当我憋第三篇的内容时候一直没有灵感,因此先打算放一放。

  

本篇文章与源码原本打算实在去年的时候完成并发布的,然而我一直忙于公司项目的微服务的实施,所以该篇文章一拖再拖。如今我花了点时间整理了下代码,并以此篇文章描述整个实现思路,并开放了源码给予需要的人一些参考。

  

源码:https://github.com/SkyChenSky/Sikiro.RBAC


RBAC

  

Role-Based Access Contro翻译成中文就是基于角色的访问控制,文章以下我都用他的简称RBAC来描述。

  

现信息系统的权限控制大多数采取RBAC的思想进行实现,其本质思想是对系统各种的操作权限不是直接授予具体的某个用户,而是在用户集合与权限集合之间建立一个角色,作为间接关联。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。

  

通过以上的描述,我们可以分析出以下信息:


  •   用户与权限是通过角色间接关联的
  •   角色的本质就是权限组(权限集合)

  

这样做的好处在于,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理,减少系统的开销。

image.png

  

功能分析


权限分类


从权限的作用可以分为三种,功能权限、访问权限、数据权限


  • 功能权限
  • 功能权限指系统用户允许在页面进行按钮操作的权限。如果有权限则功能按钮展示,否则隐藏。
  • 访问权限
  • 访问权限指系统用户通过点击按钮后进行地址的请求访问的权限(地址跳转与接口请求),如果无权限访问,则由页面提示无权限访问。
  • 数据权限
  • 数据权限指用户可访问系统的数据权限,不同的用户可以访问不同的数据粒度。

数据权限的实现可大可小,大可大到对条件进行动态配置,小可小到只针对某个维度进行

硬编码。不纳入这次的讨论范围。


用例图


image.png


非功能性需求

  

时效性,直接影响到安全性,既然是权限控制,那么理应一修改权限后就立刻生效。曾经有同行问过我,是不是每一个请求都得去查一次数据库是否满足权限,如果是,数据库压力岂不是很大?

  

安全性,每一个页面跳转,每一个读写请求都的进行一次权限验证,不满足的权限的功能按钮就不需要渲染,避免样式display:none的情况。

  

开发效率,权限控制理应是框架层面的,因此尽可能作为非业务的侵入性,让开发人员保持原有的数据善增改查与页面渲染。


技术选型


LayUI

  

学习门槛极低,开箱即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到?API 的每一处细节都经过精心雕琢,非常适合界面的快速开发,它更多是为服务端程序员量身定做,无需涉足各种前端工具的复杂配置,只需面对浏览器本身,让一切你所需要的元素与交互,从这里信手拈来。作为国人的开源项目,完整的接口文档与Demo示例让入门者非常友好的上手,开箱即用的Api让学习成本尽可能的低,其易用性成为快速开发框架的基础。


MongoDB

  

主要两大优势,无模式与横向扩展。对于权限模块来说,无需SQL来写复杂查询和报表,也不需要使用到多表的强事务,上面提到的时效性的数据库压力问题也可以通过分片解决。无模式使得开发人员无需预定义存储结构,结合MongoDB官方提供的驱动可以做到快速的开发。


数据库设计


?E-R图

?

image.png

  

一个管理员可以拥有多个角色,因此管理员与角色是一对多的关联;角色作为权限组的存在,又可以选择多个功能权限值与菜单,所以角色与菜单、功能权限值也是一对多的关系。


类图


image.png


Deparment与Position属于非核心,可以按照自己的实际业务进行扩展。


功能权限值初始化

  

随着业务发展,需求功能是千奇百怪的,根本无法抽象出来,那么功能按钮就要随着业务进行定义。在我的项目里使用了枚举值进行定义每个功能权限,通过自定义的PermissionAttribute与响应的action进行绑定,在系统启动时,通过反射把功能权限的枚举值与相应的controller、action映射到MenuAction表,枚举值对应code字段,controller与action拼接后对应url字段。

  

已初始化到数据库的权限值可以到菜单页把相对应的菜单与权限通过用户界面关联起来。


权限值绑定action


1         [HttpPost]
2         [Permission(PermCode.Administrator_Edit)]
3         public IActionResult Edit(EditModel edit)
4         {
5             //do something
6
7             return Json(result);
8         }


初始化权限值


1     /// <summary>
 2     /// 功能权限
 3     /// </summary>
 4     public static class PermissionUtil
 5     {
 6         public static readonly Dictionary<string, IEnumerable<int>> PermissionUrls = new Dictionary<string, IEnumerable<int>>();
 7         private static MongoRepository _mongoRepository;
 8
 9         /// <summary>
10         /// 判断权限值是否被重复使用
11         /// </summary>
12         public static void ValidPermissions()
13         {
14             var codes = Enum.GetValues(typeof(PermCode)).Cast<int>();
15             var dic = new Dictionary<int, int>();
16             foreach (var code in codes)
17             {
18                 if (!dic.ContainsKey(code))
19                     dic.Add(code, 1);
20                 else
21                     throw new Exception($"权限值 {code} 被重复使用,请检查 PermCode 的定义");
22             }
23         }
24
25         /// <summary>
26         /// 初始化添加预定义权限值
27         /// </summary>
28         /// <param name="app"></param>
29         public static void InitPermission(IApplicationBuilder app)
30         {
31             //验证权限值是否重复
32             ValidPermissions();
33
34             //反射被标记的Controller和Action
35             _mongoRepository = (MongoRepository)app.ApplicationServices.GetService(typeof(MongoRepository));
36
37             var permList = new List<MenuAction>();
38             var actions = typeof(PermissionUtil).Assembly.GetTypes()
39                 .Where(t => typeof(Controller).IsAssignableFrom(t) && !t.IsAbstract)
40                 .SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly));
41
42             //遍历集合整理信息
43             foreach (var action in actions)
44             {
45                 var permissionAttribute =
46                     action.GetCustomAttributes(typeof(PermissionAttribute), false).ToList();
47                 if (!permissionAttribute.Any())
48                     continue;
49
50                 var codes = permissionAttribute.Select(a => ((PermissionAttribute)a).Code).ToArray();
51                 var controllerName = action?.ReflectedType?.Name.Replace("Controller", "").ToLower();
52                 var actionName = action.Name.ToLower();
53
54                 foreach (var item in codes)
55                 {
56                     if (permList.Exists(c => c.Code == item))
57                     {
58                         var menuAction = permList.FirstOrDefault(a => a.Code == item);
59                         menuAction?.Url.Add($"{controllerName}/{actionName}".ToLower());
60                     }
61                     else
62                     {
63                         var perm = new MenuAction
64                         {
65                             Id = item.ToString().EncodeMd5String().ToObjectId(),
66                             CreateDateTime = DateTime.Now,
67                             Url = new List<string> { $"{controllerName}/{actionName}".ToLower() },
68                             Code = item,
69                             Name = ((PermCode)item).GetDisplayName() ?? ((PermCode)item).ToString()
70                         };
71                         permList.Add(perm);
72                     }
73                 }
74                 PermissionUrls.TryAdd($"{controllerName}/{actionName}".ToLower(), codes);
75             }
76
77             //业务功能持久化
78             _mongoRepository.Delete<MenuAction>(a => true);
79             _mongoRepository.BatchAdd(permList);
80         }
81
82         /// <summary>
83         /// 获取当前路径
84         /// </summary>
85         /// <param name="filterContext"></param>
86         /// <returns></returns>
87         public static string CurrentUrl(HttpContext filterContext)
88         {
89             var url = filterContext.Request.Path.ToString().ToLower().Trim('/');
90             return url;
91         }
92     }


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
.Net Core实战之基于角色的访问控制的设计-
2年前开源了Sikiro.RBAC系统(https://github.com/SkyChenSky/Sikiro.RBAC)但是缺少了部署流程,这次通过申请免费的ECS,重新把流程梳理了下,并整理成改篇文章。 .Net Core实战之基于角色的访问控制的设计(https://www.cnblogs.com/skychen1218/p/13053878.html)
0 0
.Net Core实战之基于角色的访问控制的设计-部署篇
2年前开源了Sikiro.RBAC系统(https://github.com/SkyChenSky/Sikiro.RBAC)但是缺少了部署流程,这次通过申请免费的ECS,重新把流程梳理了下,并整理成改篇文章。 .Net Core实战之基于角色的访问控制的设计(https://www.cnblogs.com/skychen1218/p/13053878.html)
0 0
.Net Core实战之基于角色的访问控制的设计-部署篇
2年前开源了Sikiro.RBAC系统(https://github.com/SkyChenSky/Sikiro.RBAC)但是缺少了部署流程,这次通过申请免费的ECS,重新把流程梳理了下,并整理成改篇文章。 .Net Core实战之基于角色的访问控制的设计(https://www.cnblogs.com/skychen1218/p/13053878.html)
0 0
.Net Core实战之基于角色的访问控制的设计(三)
.Net Core实战之基于角色的访问控制的设计(三)
0 0
.Net Core实战之基于角色的访问控制的设计(二)
.Net Core实战之基于角色的访问控制的设计(二)
0 0
ASP.NET Core 基于声明的访问控制到底是什么鬼?
从ASP.NET 4.x到ASP.NET Core,内置身份验证已从基于角色的访问控制(RBAC)转变为基于声明的访问控制(CBAC)。
0 0
EMQ
支持 ACL 访问控制、引入 HOCON 全新配置文件格式
11月,超轻量MQTT Broker NanoMQ 0.14版本发布,推出了ACL鉴权服务,并引入了HOCON格式的配置文件。
0 0
CentOS Stream 9通过配置sshd_config中AllowUsers实现SSH访问控制
CentOS Stream 9通过配置sshd_config中AllowUsers实现SSH访问控制
0 0
配置访问控制|学习笔记
快速学习配置访问控制
0 0
IIS安全:配置web服务器权限更好地实现访问控制
IIS安全:配置web服务器权限更好地实现访问控制
0 0
+关注
陈珙
现任SF轻小说系统架构师,10年的.Net领域经验,专注微服务架构、分布式系统设计、高并发方案与高效工程化。.Net广州俱乐部成员,博客园作者(陈珙)
文章
问答
文章排行榜
最热
最新
相关电子书
更多
开源广进,用service cataloq构造k8s服务能力中心
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载


http://www.vxiaotou.com