本文主要描述一個通用的權限系統實現思路與過程。也是對這次製做權限管理模塊的總結。html
製做此係統的初衷是爲了讓這個權限系統得以「通用」。就是生產一個web系統經過調用這個權限系統(生成的dll文件),web
就能夠實現權限管理。這個權限系統會管理已生產系統的全部用戶,菜單,操做項,角色分配,權限分配,日誌等內容。數據庫
實現此功能從正常訪問和非法訪問兩個方面入手。正常訪問即用戶登陸系統後只能看到或操做本身擁有的菜單;非法訪問即框架
經過拼寫url等途徑訪問系統的某個功能;因此程序除了實現用戶登陸後獲取用戶擁有的菜單權限,更要擋住用戶的非法請求。兩數據庫設計
者缺一不可。ide
實現這個功能主要利用RBAC權限設計模型,英文(Role-Based Access Control)譯爲基於角色的權限管理又叫基於角色的url
訪問控制。spa
1.系統表:由於要達到"通用",因此這個表會記錄各個系統。其餘用戶、菜單、操做、權限表每條記錄都會對應系統代碼。操作系統
字段說明:Code —> 系統標識代碼設計
SysName —> 系統名稱
2.菜單表:記錄菜單。每一個功能當成一個菜單,菜單有url屬性,用戶經過點擊菜單來訪問對應功能;
字段說明:ID —> 主鍵,自增標識
MenuName —> 菜單名稱
PageUrl —> 菜單對應url
PId —> 菜單父級Id
Lv —> 菜單等級,分一級菜單和二級菜單
ControllerAction —> 菜單惟一標識,用來作權限控制
SystemCode —> 系統標識代碼
3.操做表:此表主要是爲了判斷用戶是否有來操做某個具體功能,如經常使用的【刪除】功能等操做都放在這個表裏;
字段說明:ID —> 主鍵,自增標識
OprateName —> 操做名稱
OperateCode —> 操做標識代碼
SystemCode —> 系統標識代碼
4.用戶表:記錄全部系統的使用用戶。記錄用戶帳號、密碼等信息;
字段說明:ID —> 主鍵,自增標識
UserName —> 用戶登陸名稱
UserPwd —> 用戶登陸密碼
SystemCode —> 系統標識代碼
5.角色表:這是RBAC設計不可缺乏的,記錄角色信息,不一樣級別的角色擁有不一樣的權限。給用戶分配角色也用到它;
字段說明:ID —> 主鍵,自增標識
RoleName —> 角色名稱
SystemCode —> 系統標識代碼
6.權限表:存放用戶的權限信息。在這個系統裏個人設計是每一個菜單,每一個操做都對應一個權限記錄。因此有一個字段[ActionType]
來區分是菜單仍是操做;
字段說明:ID —> 主鍵,自增標識
PowerName —> 權限名稱
ActionId —> 菜單或操做ID
ActionType —> 區分是菜單或操做
SystemCode —> 系統標識代碼
7.用戶對應角色表:這個表負責關聯用戶和角色的關係。因爲初次使用RBAC模型,又爲了快速投入使用,所以在此次使用中沒有
考慮一對多的角色。因此在這個系統裏用戶和角色都是一對一的關係,每一個用戶對應一個角色;
字段說明:ID —> 主鍵,自增標識
UserId —> 用戶ID
RoleId —> 角色ID
SystemCode —> 系統標識代碼
8.角色對應權限表:這個表負責管理角色和權限的關係。角色和權限屬於一對多的關係,每一個角色對應多個權限;
字段說明:ID —> 主鍵,自增標識
RoleId —> 角色ID
PowerId —> 權限ID
SystemCode —> 系統標識代碼
9.日誌表:這個應該容易理解,日誌表記錄用戶操做系統的痕跡,像用戶信息、訪問url、時間等。
字段說明:ID —> 主鍵,自增標識
UserId —> 操做名稱
VisitUrl —> 操做標識代碼
Remark —> 系統標識代碼
CreateTime —> 建立時間
SystemCode —> 系統標識代碼
三.程序設計
上面說了數據庫的構造,接下來說一下這次程序的設計方案。開篇已經提過爲了通用此項目最終生成dll文件以便其餘系統調用,
固然你也能夠作成webservice,還能知足跨平臺。
這個系統不與任何業務系統公用數據訪問層和業務邏輯層。即便二者使用一個相同的數據庫,這個系統仍是擁有獨立的數據訪
問層和業務邏輯層,當然這個狀況只限於一個實現權限管理的「權限後臺」。
在此我用了三層,添加了一個接口Service。其餘系統只能訪問這個接口調用本身須要的方法。這樣作對於系統自己有利於避免
方法被任意調用,功能實現途徑不一。對使用者也簡潔明瞭。爲了達到這個目的能夠用關鍵字internal修飾數據訪問層和業務邏輯層
的類。程序結構以下圖:
繼續說說這個接口提供了哪些方法和屬性吧。
1.當前登錄用戶:在系統中很多地方要用到它,好比展現登錄用戶的帳號,角色等,存放登陸用戶可使用Session,也可以使用Redis;
2.登陸/退出方法;
2.登錄用戶有權限訪問的菜單集合:提供使用用戶正常訪問的菜單,例如樹形菜單等;
3.子菜單集合:方法2也可實現,這個只不過多一個服務而已;
4.判斷操做權限的方法:數據庫中有一個操做表,經過操做代碼去權限表裏查詢。判斷登錄用戶是否擁有某一個操做的權限,例如
「刪除」功能;
5.日誌集合:知足各個系統查看日誌的須要。
四.權限和日誌
這裏的權限是爲了擋住非正常途徑的訪問。實現思路是經過用戶訪問的url跟用戶能夠訪問的菜單屬性【ControllerAction】一一
對比。爲了便於理解,在此用開發實例說明一下:
我作業務系統時UI框架用的是MVC4.0,他很好的支持Filter。所以在權限系統三層的基礎上加了一個Filter文件夾,建立PowerAttribute
類,提供驗證登陸和驗證權限兩個類。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class LoginAttribute : ActionFilterAttribute { /// <summary> 登陸驗證 </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { var loginUser = UserService.LoginUser; base.OnActionExecuting(filterContext); if (loginUser.ID <= 0) { filterContext.Result = new RedirectResult("/home/login"); filterContext.Result.ExecuteResult(filterContext); } } }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class PowerAttribute : LoginAttribute { /// <summary> 訪問權限驗證 </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { LoginAttribute loginAttribute = new LoginAttribute(); loginAttribute.OnActionExecuting(filterContext); var routeAction = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"] + "." +
HttpContext.Current.Request.RequestContext.RouteData.Values["Action"]; List<Menu> userOwnMenuList = MenuService.GetUserOwnMenuList().ResultModel as List<Menu> ?? new List<Menu>(); var isHavepermission = userOwnMenuList.FirstOrDefault(m => m.ControllerAction.ToLower() == routeAction.ToLower()) != null; if (!isHavepermission) { HttpContext.Current.Response.Write("您沒有權限訪問"); HttpContext.Current.Response.End(); } } }
訪問【用戶列表】時,只需在對應的Action上添加特性Power便可。
// GET: /User/List // 用戶列表頁 [Power] [Log(Desc = "查看用戶列表")] public ActionResult List(string code = "", int page = 1)
日誌實現同上,就再也不敘述了。整篇文字偏多,沒有寫實現接口的代碼,是由於這些方法實現都很基礎。不便登大雅之堂。