溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

發(fā)布時間:2020-07-06 12:56:43 來源:網(wǎng)絡(luò) 閱讀:801 作者:wuhuacong 欄目:開發(fā)技術(shù)

我在上一篇隨筆《基于MVC4+EasyUI的Web開發(fā)框架形成之旅--框架總體界面介紹》中大概介紹了基于MVC的Web開發(fā)框架的權(quán)限控制總體思路。其中的權(quán)限控制就是分為“用戶登錄身份驗證”、“控制器方法權(quán)限控制”、“界面元素權(quán)限控制”三種控制方式,可以為Web開發(fā)框架本身提供了很好用戶訪問控制和權(quán)限控制,使得用戶界面呈現(xiàn)菜單、Web界面的按鈕和內(nèi)容、Action的提交控制,均能在總體權(quán)限功能分配和控制之下。

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

本篇文章主要細化這三個方面的介紹,重點介紹“控制器方法權(quán)限控制”、“界面元素權(quán)限控制”這兩種權(quán)限控制方式。

 1、用戶登錄控制

登錄界面如下所示。

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

其中登錄的前臺頁面代碼如下所示,其中可以在登錄界面接收驗證碼(如果必要的話)。

//實現(xiàn)用戶登錄
function LoginUserInfo() {
	//獲取單擊用戶登錄按鈕的事件
	$("#btnLogin").click(function () {
		//首先獲取到要傳遞到控制器的參數(shù),并且狗造成Json。UserName,UserPassword,Code
		var postData = {
			UserName: $("#UserName").val(),
			Password: $("#Password").val(),
			Code: $("#Code").val()
		};

		//發(fā)送異步請求實現(xiàn)登錄 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 取得用戶的授權(quán)信息,并存儲在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 = "用戶名輸入錯誤或者您已經(jīng)被禁用";
		}
	}

	return Content(result);
}

從上面的代碼,我們可以看到,在用戶登錄成功后,后臺把用戶信息、用戶權(quán)限列表信息放到了Session里面,方便進行后面的權(quán)限控制。

然后當前端頁面獲得成功響應(yīng)并切換到Home的Index視圖前,后臺會調(diào)用Home的控制器,把一些用戶信息放到了ViewBag對象里面,并構(gòu)造用戶的相關(guān)菜單項目,代碼如下所示。

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();            
	}

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

 

2、控制器方法權(quán)限控制

 我們知道,對頁面的權(quán)限控制,可以分為前端控制和后臺代碼的控制,控制器方法的權(quán)限控制屬于后臺代碼的控制。為了方便基類代碼的權(quán)限控制,我們定義一個權(quán)限控制鍵的類,用來記錄通用的增加、修改、刪除、查看、列表、導(dǎo)出等傳統(tǒng)控制元素,代碼如下所示。

/// <summary>
/// 定義常用功能的控制ID,方便基類控制器對用戶權(quán)限的控制
/// </summary>
[DataContract]
[Serializable]
public class AuthorizeKey
{
	#region 常規(guī)功能控制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>
	/// 導(dǎo)出記錄的功能控制ID
	/// </summary>
	public string ExportKey { get; set; } 
	#endregion

	#region 常規(guī)權(quán)限判斷
	/// <summary>
	/// 判斷是否具有插入權(quán)限
	/// </summary>
	public bool CanInsert { get; set; }

	/// <summary>
	/// 判斷是否具有更新權(quán)限
	/// </summary>
	public bool CanUpdate { get; set; }

	/// <summary>
	/// 判斷是否具有刪除權(quán)限
	/// </summary>
	public bool CanDelete { get; set; }

	/// <summary>
	/// 判斷是否具有列表權(quán)限
	/// </summary>
	public bool CanList { get; set; }

	/// <summary>
	/// 判斷是否具有查看權(quán)限
	/// </summary>
	public bool CanView { get; set; }

	/// <summary>
	/// 判斷是否具有導(dǎo)出權(quán)限
	/// </summary>
	public bool CanExport { get; set; }

	#endregion

	/// <summary>
	/// 默認構(gòu)造函數(shù)
	/// </summary>
	public AuthorizeKey() { }

	/// <summary>
	/// 常用構(gòu)造函數(shù)
	/// </summary>
	public AuthorizeKey(string insert, string update, string delete, string view = "") 
	{
		this.InsertKey = insert;
		this.UpdateKey = update;
		this.DeleteKey = delete;
		this.ViewKey = view;
	}
}

有了這個實體類,我們就可以在控制器的基類BaseController里面實現(xiàn)一些控制邏輯了。首先我們在控制器每次執(zhí)行方法前,都對權(quán)限進行一個轉(zhuǎn)換,并把控制鍵存儲到ViewBage里面,方便前端頁面的控制,如下代碼所示。

/// <summary>
/// 重新基類在Action執(zhí)行之前的事情
/// </summary>
/// <param name="filterContext">重寫方法的參數(shù)</param>
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
	base.OnActionExecuting(filterContext);

	//得到用戶登錄的信息
	CurrentUser = Session["UserInfo"] as UserInfo;            
	if (CurrentUser == null)
	{
		Response.Redirect("/Login/Index");//如果用戶為空跳轉(zhuǎn)到登錄界面
	}

	//設(shè)置授權(quán)屬性,然后賦值給ViewBag保存
	ConvertAuthorizedInfo();
	ViewBag.AuthorizeKey = AuthorizeKey;
}

其中ConvertAuthorizedInfo()函數(shù)是驗證登陸用戶是否具有相應(yīng)的權(quán)限的。

/// <summary>
/// 對AuthorizeKey對象里面的操作權(quán)限進行賦值,用于頁面判斷
/// </summary>
protected virtual void ConvertAuthorizedInfo()
{
	//判斷用戶權(quán)限
	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的控制器基類還定義了判斷用戶是否有某些權(quán)限的邏輯,如果沒有沒有權(quán)限,就會拋出自定義異常(MyDenyAccessException),代碼如下。

/// <summary>
/// 用于檢查方法執(zhí)行前的權(quán)限,如果未授權(quán),返回MyDenyAccessException異常
/// </summary>
/// <param name="functionId"></param>
protected virtual void CheckAuthorized(string functionId)
{
	if(!HasFunction(functionId))
	{
		string errorMessage = "您未被授權(quán)使用該功能,請重新登錄測試或聯(lián)系管理員進行處理。";
		throw new MyDenyAccessException(errorMessage);
	}
}

有了上面的這些邏輯,我們在業(yè)務(wù)控制器基類(BusinessController<B, T>)里面,就可以實現(xiàn)對一些基本操作的API的權(quán)限控制了。

/// <summary>
/// 本控制器基類專門為訪問數(shù)據(jù)業(yè)務(wù)對象而設(shè)的基類
/// </summary>
/// <typeparam name="B">業(yè)務(wù)對象類型</typeparam>
/// <typeparam name="T">實體類類型</typeparam>
public class BusinessController<B, T> : BaseController
	where B : class
	where T : WHC.Framework.ControlUtil.BaseEntity, new()
{

	/// <summary>
	/// 插入指定對象到數(shù)據(jù)庫中
	/// </summary>
	/// <param name="info">指定的對象</param>
	/// <returns>執(zhí)行操作是否成功。</returns>
	public virtual ActionResult Insert(T info)
	{
		//檢查用戶是否有權(quán)限,否則拋出MyDenyAccessException異常
		base.CheckAuthorized(AuthorizeKey.InsertKey);

		bool result = false;
		if (info != null)
		{
			result = baseBLL.Insert(info);
		}
		return Content(result);
	}

	/// <summary>
	/// 更新對象屬性到數(shù)據(jù)庫中
	/// </summary>
	/// <param name="info">指定的對象</param>
	/// <param name="id">主鍵ID的值</param>
	/// <returns>執(zhí)行成功返回<c>true</c>,否則為<c>false</c>。</returns>
	public virtual ActionResult Update(string id, FormCollection formValues)
	{
		//檢查用戶是否有權(quán)限,否則拋出MyDenyAccessException異常
		base.CheckAuthorized(AuthorizeKey.UpdateKey);

		T obj = baseBLL.FindByID(id);
		if (obj != null)
		{
			//遍歷提交過來的數(shù)據(jù)(可能是實體類的部分屬性更新)
			foreach (string key in formValues.Keys)
			{
				string value = formValues[key];
				System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(key);
				if (propertyInfo != null)
				{
					try
					{
						// obj對象有key的屬性,把對應(yīng)的屬性值賦值給它(從字符串轉(zhuǎn)換為合適的類型)
						//如果轉(zhuǎn)換失敗,會拋出InvalidCastException異常
						propertyInfo.SetValue(obj, Convert.ChangeType(value, propertyInfo.PropertyType), null);
					}
					catch { }
				}
			}
		}

		bool result = baseBLL.Update(obj, id);
		return Content(result);
	}

 

3、界面元素權(quán)限控制

我們從上面那個Web開發(fā)框架的主界面圖可以看到,里面對于某個特定的業(yè)務(wù),增加、修改、、查看、刪除等操作都放在了EasyUI的DataGrid工具欄里面了,為了動態(tài)控制用戶能訪問的界面按鈕,我們需要結(jié)合用戶權(quán)限集合進行界面呈現(xiàn),首先我們把ToolBar放到一個層里面進行定義,如下代碼所示。

//實現(xiàn)對DataGird控件的綁定操作
function InitGrid(queryData) {
	$('#grid').datagrid({   //定位到Table標簽,Table標簽的ID是grid
		url: '/Information/FindWithPager',   //指向后臺的Action來獲取當前用戶的信息的Json格式的數(shù)據(jù)
		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',    //根據(jù)某個字段給easyUI排序
		sortOrder: 'asc',
		remoteSort: false,
		idField: 'ID',
		queryParams: queryData,  //異步查詢的參數(shù)
		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輔助類來實現(xiàn)界面控件代碼控制生成,因此已經(jīng)可以達到了界面權(quán)限的控制了。使用這種HTML層定義的工具條定義方式,比通過腳本定義的工具條效果少了一個分隔線,其他的都還是一致的。

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

<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>

上面使用了兩種方式來判斷用戶的權(quán)限的,一種是使用這種ViewBag對象的樹形進行判斷,如下所示。

@if (@ViewBag.AuthorizeKey.CanDelete)

還有一種是使用HTML輔助類的擴展方法進行判斷,這種方法適用于一些非常規(guī)的權(quán)限控制集合的判斷,如下所示

@if (@Html.HasFunction("Information/View"))

其中HTML輔助類方法是通過擴展靜態(tài)方法進行實現(xiàn),代碼如下所示。

public static class HtmlHelpers
{
	public static bool HasFunction(this HtmlHelper helper, string functionId)
	{
		return Permission.HasFunction(functionId);
	}

	public static bool IsAdmin()
	{
		return Permission.IsAdmin();
	}
}

上面的界面控制方法,是通過控制界面代碼的生成與否進行權(quán)限控制的,前面我們講了,通過后臺代碼的控制器方法也是可以實現(xiàn)控制,而且是拋出自定義的錯誤,那么我們在使用Ajax方法調(diào)用的時候,也可以對這個錯誤信息進行友好顯示,提示用戶權(quán)限不足,前端頁面操作代碼如下。

//綁定添加按鈕的事件
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.關(guān)閉彈出層,2.刷新DataGird
				$.messager.alert("提示", "添加成功");
				$("#DivAdd").dialog("close");
				$("#grid").datagrid("reload");
				$("#ffAdd").form("clear");

				//本頁面的類型為【通知公告】,固定不變
				$("#Category").val("通知公告");
			}
			else {
				$.messager.alert("提示", "添加失敗,請您檢查");
			}
		}).error(function () {
			$.messager.alert("提示", "您未被授權(quán)使用該功能,請聯(lián)系管理員進行處理。", 'warning');
		});
	});
}

基于MVC4+EasyUI的Web開發(fā)框架形成之旅--權(quán)限控制

 以上就是我對Web開發(fā)框架中的權(quán)限控制幾個方面的思路和代碼,希望拋磚引玉,獲得大家更好的反饋和支持。


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI