.NET Core微服务 权限系统+工作流(一)权限系统
一、前言
實際上權限系統老早之前我就在一直開發,大概在剛畢業沒多久就想一個人寫一個系統,斷斷續續一直堅持到現在,畢竟自己親動手自寫的系統才有收獲,本篇僅介紹權限。
小小系統上不了臺面,望各位大神勿噴。
二、系統介紹
目前采用的是.Net Core微服務的方式實現,本文不討論具體的中間件主要是(ocelot + consul等),一直參考微軟的??eShopOnContainers?,進行簡單的實現,但是ORM是用的Dapper,并簡單進行封裝? 傳送門 ,當然自己也封裝了一些簡單的插件進行復用:傳送門,如下:
三、權限系統
權限系統實現很簡單,權限的劃分我覺得可以分為三種:
1、菜單權限2、按鈕權限3、數據權限
簡單介紹下:1、菜單權限。表示用戶是否能夠訪問該頁面(角色掛鉤)
2、按鈕權限。表示用戶是否能夠操作該頁面上的功能(角色掛鉤)
3、數據權限。表示用戶訪問頁面時進行數據篩選(該功能暫未實現,這個要與具體的業務結合才能寫),與部門掛鉤,這個不太好理解,當然一般的權限系統這個功能也不會做,舉個簡單例子,OA系統里面我查看我的工資條,我應該只能看到我自己的數據,但是我的部門經理,他可以有權限看到該部門的全部數據,這個就是數據權限。
為什么寫這個系統?
之前待過好幾家公司,發現他們的系統都是對菜單進行分配,當然了,業務需求只要這個就當我沒說,我只是覺得這樣做太不安全并且我覺得之前系統的實現方式可以進行一些優化,所以就一直寫到現在,可能代碼質量不如哪些大神的優秀,系統在我看來太小,就簡單搭了個框架實現。你過條小水溝,沒必要造條橋。
要使用該系統前提條件:前端:Sea.js和Vue,對于sea.js,在前端這塊感覺已經沒多少人用了,但是這中CMD思想是不會被淘汰的,你看最近比較火的layerui也是的,對于Vues只是簡單的應用,也就用到雙向綁定而已,開發復雜的頁面確實比較方便,但是簡單的頁面就得不償失了。
后端:consul、rabbitmq ,具體怎么安裝不在描述
大概的用戶訪問流程描述如下:
用戶登錄? ? =====》 獲取該用戶角色 ===》 通過角色獲取該角色對應的權限?并集? ===>返回相應數據
sys_user_role sys_role_resource
?
系統關系圖如下(MySQL):
?
具體功能實現請看代碼,這里不做闡述,菜單權限的分配通過角色表和菜單表的關聯表操作即可,但是按鈕的權限分配如何實現?我的實現方式是:把按鈕的操作也看成一種菜單的資源分配,只不過比較特殊,我這里不僅僅是對按鈕的顯示進行控制,我做的比較絕,也對后臺方法訪問權限也做了控制,這樣比較安全,對于按鈕權限的控制,實際上是明確的,比方說,一個刪除按鈕,它只能對應后臺的一個刪除方法,這個方法是明確的,對于頁面的按鈕的類型和個數是固定的,不然你沒辦法分配,基于這個前提,我對菜單的生成進行代碼控制從而達到控制目的,因此,菜單和按鈕和在一起稱之為資源表 sys_resource 。具體的實現代碼也不是很復雜,一層一層判斷即可,權限過濾器如下:
1 public class PermissionAuthorizationRequirement : IAuthorizationRequirement 2 { 3 public UrlAndButtonType UrlAndButtonType { get; } 4 5 public PermissionAuthorizationRequirement(string url, ButtonType buttonType, bool isPage) 6 { 7 UrlAndButtonType = new UrlAndButtonType() 8 { 9 Url = url, 10 ButtonType = (byte)buttonType, 11 IsPage = isPage 12 }; 13 } 14 public PermissionAuthorizationRequirement(string url, byte buttonType, bool isPage) 15 { 16 UrlAndButtonType = new UrlAndButtonType() 17 { 18 Url = url, 19 ButtonType = buttonType, 20 IsPage = isPage 21 }; 22 } 23 } 24 /// <summary> 25 /// 權限過濾器 26 /// </summary> 27 [Authorize] 28 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 29 public sealed class PermissionAttribute : TypeFilterAttribute 30 { 31 /// <summary> 32 /// 構造器 33 /// </summary> 34 /// <param name="url">地址</param> 35 /// <param name="buttonType">按鈕類型</param> 36 /// <param name="isPage">是否是頁面</param> 37 public PermissionAttribute(string url = default(string), ButtonType buttonType = ButtonType.View, bool isPage = true) : 38 base(typeof(RequiresPermissionAttributeExecutor)) 39 { 40 Arguments = new object[] { new PermissionAuthorizationRequirement(url, buttonType, isPage) }; 41 } 42 /// <summary> 43 /// 構造器 44 /// </summary> 45 /// <param name="url">地址</param> 46 /// <param name="buttonType">按鈕類型</param> 47 /// <param name="isPage">是否是頁面</param> 48 public PermissionAttribute(string url, byte buttonType, bool isPage = true) : 49 base(typeof(RequiresPermissionAttributeExecutor)) 50 { 51 Arguments = new object[] { new PermissionAuthorizationRequirement(url, buttonType, isPage) }; 52 } 53 54 private class RequiresPermissionAttributeExecutor : Attribute, IAsyncResourceFilter 55 { 56 private IPermissionStorageContainer _permissionStorage; 57 private PermissionAuthorizationRequirement _requiredPermissions; 58 59 public RequiresPermissionAttributeExecutor( 60 IPermissionStorageContainer permissionStorage, PermissionAuthorizationRequirement requiredPermissions) 61 { 62 _permissionStorage = permissionStorage; 63 _requiredPermissions = requiredPermissions; 64 } 65 66 public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) 67 { 68 string menuUrl = _requiredPermissions.UrlAndButtonType.Url; 69 //判斷用戶權限 70 if (string.IsNullOrEmpty(menuUrl)) 71 { 72 //區域判斷 73 string area = context.RouteData.Values["area"].ToString(); 74 if (string.IsNullOrEmpty(area)) 75 { 76 menuUrl = "/" + context.RouteData.Values["controller"] + "/" + context.RouteData.Values["action"]; 77 } 78 else 79 { 80 menuUrl = "/" + area + "/" + context.RouteData.Values["controller"] + "/" + context.RouteData.Values["action"]; 81 } 82 } 83 menuUrl = menuUrl.Trim().ToLower(); 84 var dbpermission = await _permissionStorage.GetPermissionAsync(); 85 var menu = dbpermission.Menus.FirstOrDefault(m => m.MenuUrl != null && m.MenuUrl.Trim().ToLower() == menuUrl); 86 if (menu != null)//地址存在 87 { 88 if (_requiredPermissions.UrlAndButtonType.ButtonType == default(byte)) 89 { 90 await next(); 91 } 92 else 93 { 94 byte buttonType = (byte)_requiredPermissions.UrlAndButtonType.ButtonType; 95 if (menu.MenuButton.Select(m => m.ButtonType).Contains(buttonType))//擁有操作權限 96 { 97 await next(); 98 } 99 else 100 { 101 //沒有操作權限 102 if (_requiredPermissions.UrlAndButtonType.IsPage) 103 { 104 context.Result = new RedirectResult("/error/noauth"); 105 } 106 else 107 { 108 context.Result = new ContentResult() 109 { 110 Content = PermissionStatusCodes.Status2Unauthorized.ToString() 111 }; 112 } 113 await context.Result.ExecuteResultAsync(context); 114 } 115 } 116 } 117 else 118 { 119 //沒有操作權限 120 if (_requiredPermissions.UrlAndButtonType.IsPage) 121 { 122 context.Result = new RedirectResult("/error/noauth"); 123 } 124 else 125 { 126 context.Result = new ContentResult() 127 { 128 Content = PermissionStatusCodes.Status2Unauthorized.ToString() 129 }; 130 } 131 await context.Result.ExecuteResultAsync(context); 132 } 133 } 134 } 135 136 }在對于的頁面添加過濾器即可,如下:
1 [HttpGet] 2 [Permission] 3 public async Task<IActionResult> Index(int pageIndex=1,int pageSize=10) 4 { 5 var res = await _messageService.GetPageAsync(pageIndex, pageSize); 6 return View(res); 7 } 8 [HttpGet] 9 [Permission("/Sys/Message/Index", ButtonType.View)] 10 public IActionResult Show() 11 { 12 return View(); 13 }?系統界面展示圖:后臺模板是之前從網上找的并自己簡單改了一下,將就能看吧,實在不想花功夫在前端上面了@-^-@
運行步驟:1、確保數據庫mssystem和mssystemlog存在 github文檔中
2、consul服務啟動,如下回車運行
3、VS項目啟動
管理員登錄賬號wms,密碼:所有賬號密碼都是123
代碼地址:
https://github.com/wangmaosheng/MsSystem-BPM-ServiceAndWebApps
如果覺得有點作用的話,可以 start 下,后續會持續更新
轉載于:https://www.cnblogs.com/wms01/p/10903646.html
總結
以上是生活随笔為你收集整理的.NET Core微服务 权限系统+工作流(一)权限系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1-STM32+W5500+GPRS物联
- 下一篇: 机器学习的原理