打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
基于MVC4+EasyUI的Web开发框架形成之旅--权限控制

我在上一篇随笔《基于MVC4+EasyUI的Web开发框架形成之旅--框架总体界面介绍》中大概介绍了基于MVC的Web开发框架的权限控制总体思路。其中的权限控制就是分为“用户登录身份验证”、“控制器方法权限控制”、“界面元素权限控制”三种控制方式,可以为Web开发框架本身提供了很好用户访问控制和权限控制,使得用户界面呈现菜单、Web界面的按钮和内容、Action的提交控制,均能在总体权限功能分配和控制之下。

本篇文章主要细化这三个方面的介绍,重点介绍“控制器方法权限控制”、“界面元素权限控制”这两种权限控制方式。

 1、用户登录控制

登录界面如下所示。

其中登录的前台页面代码如下所示,其中可以在登录界面接收验证码(如果必要的话)。

        //实现用户登录        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();                    }

 

2、控制器方法权限控制

 我们知道,对页面的权限控制,可以分为前端控制和后台代码的控制,控制器方法的权限控制属于后台代码的控制。为了方便基类代码的权限控制,我们定义一个权限控制键的类,用来记录通用的增加、修改、删除、查看、列表、导出等传统控制元素,代码如下所示。

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

 

3、界面元素权限控制

我们从上面那个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" style="padding: 5px; height: auto">        <div style="margin-bottom: 5px">            @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开发框架中的权限控制几个方面的思路和代码,希望抛砖引玉,获得大家更好的反馈和支持。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
一步一步Asp.Net MVC系列_权限管理总结(附MVC权限管理系统源码)
一步一步Asp.Net MVC系列
[回馈]ASP.NET Core MVC开发实战之商城系统(二)
java web简单权限管理设计
微信公众平台开发教程(五)自定义菜单(含实例源码)
C# FTP操作类(D)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服