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

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

前言

  

上个月,我写了两篇微服务的文章:《.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     }


相关实践学习
云安全基础课 - 访问控制概述
课程大纲 课程目标和内容介绍视频时长 访问控制概述视频时长 身份标识和认证技术视频时长 授权机制视频时长 访问控制的常见攻击视频时长
目录
相关文章
|
1月前
|
消息中间件 前端开发 小程序
一个基于.NET Core构建的简单、跨平台、模块化的商城系统
今天大姚给大家分享一个基于.NET Core构建的简单、跨平台、模块化、完全开源免费(MIT License)的商城系统:Module Shop。
|
1月前
|
算法 C# 数据库
【干货】一份10万字免费的C#/.NET/.NET Core面试宝典
C#/.NET/.NET Core相关技术常见面试题汇总,不仅仅为了面试而学习,更多的是查漏补缺、扩充知识面和大家共同学习进步。该知识库主要由自己平时学习实践总结、网上优秀文章资料收集(这一部分会标注来源)和社区小伙伴提供三部分组成。该份基础面试宝典完全免费,发布两年来收获了广大.NET小伙伴的好评,我会持续更新和改进,欢迎关注我的公众号【追逐时光者】第一时间获取最新更新的面试题内容。
|
1月前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
12天前
|
开发框架 .NET 中间件
七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
|
18天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
1月前
|
Linux API iOS开发
.net core 优势
.NET Core 的优势:跨平台兼容(Windows, macOS, Linux)及容器支持,高性能,支持并行版本控制,丰富的新增API,以及开源。
27 4
|
1月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。
|
2月前
|
Windows
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
232 0
|
2月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
85 0
|
3月前
|
开发框架 前端开发 .NET
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
为了便于大家查找,特将之前开发的.Net Core相关的五大案例整理成文,共计440页,32w字,免费提供给大家,文章底部有PDF下载链接。
39 1
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!