您好,登錄后才能下訂單哦!
我在上一篇隨筆《基于MVC4+EasyUI的Web開發框架形成之旅--框架總體界面介紹》中大概介紹了基于MVC的Web開發框架的權限控制總體思路。其中的權限控制就是分為“用戶登錄身份驗證”、“控制器方法權限控制”、“界面元素權限控制”三種控制方式,可以為Web開發框架本身提供了很好用戶訪問控制和權限控制,使得用戶界面呈現菜單、Web界面的按鈕和內容、Action的提交控制,均能在總體權限功能分配和控制之下。
本篇文章主要細化這三個方面的介紹,重點介紹“控制器方法權限控制”、“界面元素權限控制”這兩種權限控制方式。
登錄界面如下所示。
其中登錄的前臺頁面代碼如下所示,其中可以在登錄界面接收驗證碼(如果必要的話)。
//實現用戶登錄 function LoginUserInfo() { //獲取單擊用戶登錄按鈕的事件 $("#btnLogin").click(function () { //首先獲取到要傳遞到控制器的參數,并且狗造成Json。UserName,UserPassword,Code var postData = { UserName: $("#UserName").val(), Password: $("#Password").val(), Code: $("#Code").val() }; //發送異步請求實現登錄 ajax $.ajax({ url: '/Login/CheckUser', data: postData, cache: false, async: true, type: 'post', success: function (data) { if (data == "OK") { window.location.href = "/Home/Index"; } else { alert(data); window.location.href = "/Login/Index"; } } }); }); }
用戶登錄的后臺控制器方法如下所示:
/// <summary> /// 對用戶登錄的操作進行驗證 /// </summary> /// <param name="username">用戶賬號</param> /// <param name="password">用戶密碼</param> /// <param name="code">驗證碼</param> /// <returns></returns> public ActionResult CheckUser(string username, string password, string code) { string result = ""; bool codeValidated = true; if (this.TempData["ValidateCode"] != null) { codeValidated = (this.TempData["ValidateCode"].ToString() == code); } if (string.IsNullOrEmpty(username)) { result = "用戶名不能為空"; } else if (!codeValidated) { result = "驗證碼輸入有誤"; } else { string ip = GetClientIp(); string macAddr = ""; string identity = BLLFactory<WHC.Security.BLL.User>.Instance.VerifyUser(username, password, MyConstants.SystemType, ip, macAddr); if (!string.IsNullOrEmpty(identity)) { UserInfo info = BLLFactory<WHC.Security.BLL.User>.Instance.GetUserByName(username); if (info != null) { result = "OK"; Session["UserInfo"] = info; Session["Identity"] = info.Name.Trim(); #region 取得用戶的授權信息,并存儲在Session中 List<FunctionInfo> functionList = BLLFactory<Function>.Instance.GetFunctionsByUser(info.ID, MyConstants.SystemType); Dictionary<string, string> functionDict = new Dictionary<string, string>(); foreach (FunctionInfo functionInfo in functionList) { if (!string.IsNullOrEmpty(functionInfo.ControlID) && !functionDict.ContainsKey(functionInfo.ControlID)) { functionDict.Add(functionInfo.ControlID, functionInfo.ControlID); } } Session["Functions"] = functionDict; #endregion } } else { result = "用戶名輸入錯誤或者您已經被禁用"; } } return Content(result); }
從上面的代碼,我們可以看到,在用戶登錄成功后,后臺把用戶信息、用戶權限列表信息放到了Session里面,方便進行后面的權限控制。
然后當前端頁面獲得成功響應并切換到Home的Index視圖前,后臺會調用Home的控制器,把一些用戶信息放到了ViewBag對象里面,并構造用戶的相關菜單項目,代碼如下所示。
public class HomeController : BaseController { public ActionResult Index() { if (CurrentUser != null) { ViewBag.FullName = CurrentUser.FullName; ViewBag.Name = CurrentUser.Name; StringBuilder sb = new StringBuilder(); List<MenuInfo> menuList = BLLFactory<Menu>.Instance.GetTopMenu(MyConstants.SystemType); int i = 0; foreach (MenuInfo menuInfo in menuList) { sb.Append(GetMenuItemString(menuInfo, i)); i++; } ViewBag.HeaderScript = sb.ToString();//一級菜單代碼 } return View(); }
我們知道,對頁面的權限控制,可以分為前端控制和后臺代碼的控制,控制器方法的權限控制屬于后臺代碼的控制。為了方便基類代碼的權限控制,我們定義一個權限控制鍵的類,用來記錄通用的增加、修改、刪除、查看、列表、導出等傳統控制元素,代碼如下所示。
/// <summary> /// 定義常用功能的控制ID,方便基類控制器對用戶權限的控制 /// </summary> [DataContract] [Serializable] public class AuthorizeKey { #region 常規功能控制ID /// <summary> /// 新增記錄的功能控制ID /// </summary> public string InsertKey { get; set; } /// <summary> /// 更新記錄的功能控制ID /// </summary> public string UpdateKey { get; set; } /// <summary> /// 刪除記錄的功能控制ID /// </summary> public string DeleteKey { get; set; } /// <summary> /// 查看列表的功能控制ID /// </summary> public string ListKey { get; set; } /// <summary> /// 查看明細的功能控制ID /// </summary> public string ViewKey { get; set; } /// <summary> /// 導出記錄的功能控制ID /// </summary> public string ExportKey { get; set; } #endregion #region 常規權限判斷 /// <summary> /// 判斷是否具有插入權限 /// </summary> public bool CanInsert { get; set; } /// <summary> /// 判斷是否具有更新權限 /// </summary> public bool CanUpdate { get; set; } /// <summary> /// 判斷是否具有刪除權限 /// </summary> public bool CanDelete { get; set; } /// <summary> /// 判斷是否具有列表權限 /// </summary> public bool CanList { get; set; } /// <summary> /// 判斷是否具有查看權限 /// </summary> public bool CanView { get; set; } /// <summary> /// 判斷是否具有導出權限 /// </summary> public bool CanExport { get; set; } #endregion /// <summary> /// 默認構造函數 /// </summary> public AuthorizeKey() { } /// <summary> /// 常用構造函數 /// </summary> public AuthorizeKey(string insert, string update, string delete, string view = "") { this.InsertKey = insert; this.UpdateKey = update; this.DeleteKey = delete; this.ViewKey = view; } }
有了這個實體類,我們就可以在控制器的基類BaseController里面實現一些控制邏輯了。首先我們在控制器每次執行方法前,都對權限進行一個轉換,并把控制鍵存儲到ViewBage里面,方便前端頁面的控制,如下代碼所示。
/// <summary> /// 重新基類在Action執行之前的事情 /// </summary> /// <param name="filterContext">重寫方法的參數</param> protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); //得到用戶登錄的信息 CurrentUser = Session["UserInfo"] as UserInfo; if (CurrentUser == null) { Response.Redirect("/Login/Index");//如果用戶為空跳轉到登錄界面 } //設置授權屬性,然后賦值給ViewBag保存 ConvertAuthorizedInfo(); ViewBag.AuthorizeKey = AuthorizeKey; }
其中ConvertAuthorizedInfo()函數是驗證登陸用戶是否具有相應的權限的。
/// <summary> /// 對AuthorizeKey對象里面的操作權限進行賦值,用于頁面判斷 /// </summary> protected virtual void ConvertAuthorizedInfo() { //判斷用戶權限 AuthorizeKey.CanInsert = HasFunction(AuthorizeKey.InsertKey); AuthorizeKey.CanUpdate = HasFunction(AuthorizeKey.UpdateKey); AuthorizeKey.CanDelete = HasFunction(AuthorizeKey.DeleteKey); AuthorizeKey.CanView = HasFunction(AuthorizeKey.ViewKey); AuthorizeKey.CanList = HasFunction(AuthorizeKey.ListKey); AuthorizeKey.CanExport = HasFunction(AuthorizeKey.ExportKey); }
其中BaseController的控制器基類還定義了判斷用戶是否有某些權限的邏輯,如果沒有沒有權限,就會拋出自定義異常(MyDenyAccessException),代碼如下。
/// <summary> /// 用于檢查方法執行前的權限,如果未授權,返回MyDenyAccessException異常 /// </summary> /// <param name="functionId"></param> protected virtual void CheckAuthorized(string functionId) { if(!HasFunction(functionId)) { string errorMessage = "您未被授權使用該功能,請重新登錄測試或聯系管理員進行處理。"; throw new MyDenyAccessException(errorMessage); } }
有了上面的這些邏輯,我們在業務控制器基類(BusinessController<B, T>)里面,就可以實現對一些基本操作的API的權限控制了。
/// <summary> /// 本控制器基類專門為訪問數據業務對象而設的基類 /// </summary> /// <typeparam name="B">業務對象類型</typeparam> /// <typeparam name="T">實體類類型</typeparam> public class BusinessController<B, T> : BaseController where B : class where T : WHC.Framework.ControlUtil.BaseEntity, new() { /// <summary> /// 插入指定對象到數據庫中 /// </summary> /// <param name="info">指定的對象</param> /// <returns>執行操作是否成功。</returns> public virtual ActionResult Insert(T info) { //檢查用戶是否有權限,否則拋出MyDenyAccessException異常 base.CheckAuthorized(AuthorizeKey.InsertKey); bool result = false; if (info != null) { result = baseBLL.Insert(info); } return Content(result); } /// <summary> /// 更新對象屬性到數據庫中 /// </summary> /// <param name="info">指定的對象</param> /// <param name="id">主鍵ID的值</param> /// <returns>執行成功返回<c>true</c>,否則為<c>false</c>。</returns> public virtual ActionResult Update(string id, FormCollection formValues) { //檢查用戶是否有權限,否則拋出MyDenyAccessException異常 base.CheckAuthorized(AuthorizeKey.UpdateKey); T obj = baseBLL.FindByID(id); if (obj != null) { //遍歷提交過來的數據(可能是實體類的部分屬性更新) foreach (string key in formValues.Keys) { string value = formValues[key]; System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(key); if (propertyInfo != null) { try { // obj對象有key的屬性,把對應的屬性值賦值給它(從字符串轉換為合適的類型) //如果轉換失敗,會拋出InvalidCastException異常 propertyInfo.SetValue(obj, Convert.ChangeType(value, propertyInfo.PropertyType), null); } catch { } } } } bool result = baseBLL.Update(obj, id); return Content(result); }
我們從上面那個Web開發框架的主界面圖可以看到,里面對于某個特定的業務,增加、修改、、查看、刪除等操作都放在了EasyUI的DataGrid工具欄里面了,為了動態控制用戶能訪問的界面按鈕,我們需要結合用戶權限集合進行界面呈現,首先我們把ToolBar放到一個層里面進行定義,如下代碼所示。
//實現對DataGird控件的綁定操作 function InitGrid(queryData) { $('#grid').datagrid({ //定位到Table標簽,Table標簽的ID是grid url: '/Information/FindWithPager', //指向后臺的Action來獲取當前用戶的信息的Json格式的數據 title: '通知公告', iconCls: 'icon-view', height: 650, width: function () { return document.body.clientWidth * 0.9 },//自動寬度 nowrap: true, autoRowHeight: true, striped: true, collapsible: true, pagination: true, pageSize: 50, pageList: [50, 100, 200], rownumbers: true, //sortName: 'ID', //根據某個字段給easyUI排序 sortOrder: 'asc', remoteSort: false, idField: 'ID', queryParams: queryData, //異步查詢的參數 columns: [[ { field: 'ck', checkbox: true }, //選擇 { title: '標題', field: 'Title', width: 350, sortable: true }, { title: '編輯者', field: 'Editor', width: 80, sortable: true }, { title: '編輯時間', field: 'EditTime', width: 150, sortable: true }, { title: '附件', field: 'Attachment_GUID', width: 250, sortable: true } ]], toolbar: "#gridtoolbar",
然后在HTML里面添加gridtoolbar的層定義,作為easyUI的表格控件的工具條。由于使用了HTML輔助類來實現界面控件代碼控制生成,因此已經可以達到了界面權限的控制了。使用這種HTML層定義的工具條定義方式,比通過腳本定義的工具條效果少了一個分隔線,其他的都還是一致的。
<div id="gridtoolbar" > <div > @if (@ViewBag.AuthorizeKey.CanInsert) { @Html.ActionLink("添加", null, null, new {onclick="ShowAddDialog()", data_options="iconCls:'icon-add', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) } @if (@ViewBag.AuthorizeKey.CanUpdate) { @Html.ActionLink("修改", null, null, new {onclick="ShowEditOrViewDialog()", data_options="iconCls:'icon-edit', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) } @if (@ViewBag.AuthorizeKey.CanDelete) { @Html.ActionLink("刪除", null, null, new {onclick="Delete()", data_options="iconCls:'icon-remove', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) } @if (@Html.HasFunction("Information/View")) { @Html.ActionLink("查看", null, null, new {onclick="ShowEditOrViewDialog('view')", data_options="iconCls:'icon-table', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) } @Html.ActionLink("刷新", null, null, new {onclick="$('#grid').datagrid('reload');", data_options="iconCls:'icon-reload', plain:true", @class = "easyui-linkbutton", href="javascript:void(0)"}) </div> </div>
上面使用了兩種方式來判斷用戶的權限的,一種是使用這種ViewBag對象的樹形進行判斷,如下所示。
@if (@ViewBag.AuthorizeKey.CanDelete)
還有一種是使用HTML輔助類的擴展方法進行判斷,這種方法適用于一些非常規的權限控制集合的判斷,如下所示
@if (@Html.HasFunction("Information/View"))
其中HTML輔助類方法是通過擴展靜態方法進行實現,代碼如下所示。
public static class HtmlHelpers { public static bool HasFunction(this HtmlHelper helper, string functionId) { return Permission.HasFunction(functionId); } public static bool IsAdmin() { return Permission.IsAdmin(); } }
上面的界面控制方法,是通過控制界面代碼的生成與否進行權限控制的,前面我們講了,通過后臺代碼的控制器方法也是可以實現控制,而且是拋出自定義的錯誤,那么我們在使用Ajax方法調用的時候,也可以對這個錯誤信息進行友好顯示,提示用戶權限不足,前端頁面操作代碼如下。
//綁定添加按鈕的事件 function BindAddEvent() { $("#btnAddOK").click(function () { //判斷表單的信息是否通過驗證 var validate = $("#ffAdd").form('validate'); if (validate == false) { return false; } var postData = $("#ffAdd").serializeArray(); $.post("/Information/Insert", postData, function (data) { if (data = "true") { //添加成功 1.關閉彈出層,2.刷新DataGird $.messager.alert("提示", "添加成功"); $("#DivAdd").dialog("close"); $("#grid").datagrid("reload"); $("#ffAdd").form("clear"); //本頁面的類型為【通知公告】,固定不變 $("#Category").val("通知公告"); } else { $.messager.alert("提示", "添加失敗,請您檢查"); } }).error(function () { $.messager.alert("提示", "您未被授權使用該功能,請聯系管理員進行處理。", 'warning'); }); }); }
以上就是我對Web開發框架中的權限控制幾個方面的思路和代碼,希望拋磚引玉,獲得大家更好的反饋和支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。