MVC3权限设计详解

1.设计过滤器:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public class OperationCheckAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 是否异步触发的验证。
        /// 如果前台用ajax执行Action,IsAsync=true;否则IsAsync=false。
        /// 这样做的目的是当没有权限时给出不同的提示方式.
        /// (直接点Url打开页面时没有权限就跳转到没有权限提示页面,
        /// ajax点击按钮时就以弹出框形式提示)
        /// 
        /// </summary>
        public bool IsAsync { get; set; }


        /// <summary>
        /// 操作代码1101,1102,1103
        /// </summary>
        public string OpCode { get; set; }

        /// <summary>
        /// 模块操作代码
        /// </summary>
        public string OpModel { get; set; }

        /// <summary>
        /// 标记Key
        /// </summary>
        public string PmKey { get; set; }

        /// <summary>
        /// 标记Key
        /// </summary>
        public string PmValue { get; set; }

        /// <summary>
        /// 运算类型= true 或 != false
        /// </summary>
        public bool IsNull { get; set; }

        private bool actionCheck = false;
        /// <summary>
        /// Action方法执行前执行
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!string.IsNullOrEmpty(PmKey))
            {
                //PmKey 不为空 PmValue 为空时,确定一个Action方法有多个权限判断
                string value =null;
                if(filterContext.ActionParameters[PmKey]!=null)
                    value = Convert.ToString(filterContext.ActionParameters[PmKey]);
                if (PmValue == null)
                {
                    if (string.IsNullOrWhiteSpace(value) && IsNull)
                    {
                        actionCheck = true;
                    }
                    else if (!string.IsNullOrWhiteSpace(value) && !IsNull)
                    {
                        actionCheck = true;
                    }
                }
                //PmKey 不为空 PmValue 不为空时,确定一个Action方法有多个权限判断
                else if (!value.Equals(null))
                {
                    if (value.ToLower() == PmValue.ToLower())
                        actionCheck = true;
                }
            }
            else if (!string.IsNullOrEmpty(OpCode))
            {
                actionCheck = true;
            }

            if (actionCheck)
            {
                bool isViewPage = true;
                if (filterContext.HttpContext.Request.HttpMethod.ToUpper() == "POST")
                { isViewPage = false; }

                UserManageBusiness userManage = new UserManageBusiness();
                TSysUser model = HttpContext.Current.Session["_sso_elab_user_"] as TSysUser;
                if (model != null)
                {
                    bool isOk = userManage.AuthCheck(model.UserID, OpCode);//权限验证
                    if (!isOk)
                    {
                        if (isViewPage)
                            filterContext.Result = new RedirectResult("~/Home/PurviewCheck");
                        else
                            filterContext.Result = new ContentResult { Content = "0" };//为前端是用ajax提交,返回是JsonResult的提供无权限处理
                    }
                }
                else
                {
                    filterContext.Result = new RedirectResult("~/Home/PurviewCheck");
                }
            }
            base.OnActionExecuting(filterContext);
        }

        /// <summary>
        /// Action方法执行后返回前执行
        /// 处理界面按钮显示
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            actionCheck = false;
            
            UserManageBusiness userManage = new UserManageBusiness();
            OperationBusiness operationBusiness = new OperationBusiness();
            string sAction = filterContext.RouteData.Values["action"].ToString();
            TSysUser model = HttpContext.Current.Session["_sso_elab_user_"] as TSysUser;
            if (model != null)
            {
                if (!string.IsNullOrEmpty(OpModel))
                {
                    TSysRoleUserMapping mapping = userManage.FindRoleUserMap(model.UserID);
                    DataTable dt = operationBusiness.FindListMarkedByRole(mapping.RoleID);
                    DataRow[] dr = null;
                    if (OpModel == "00")
                    {
                        dr = dt.Select("");
                    }
                    else
                    {
                        dr = dt.Select("OpModel=" + OpModel);
                    }
                    foreach (DataRow item in dr)
                    {
                        //审批页面特殊处理:只能让域配置的区域管理员课件
                        if (item["OpCode"].ToString()=="1105")
                        {
                            filterContext.Controller.ViewData.Add("Limited" + item["OpCode"],
                                userManage.ApproveLimited(model.UserID, model.DomainID) ? "" : "display:none");
                        }
                        else
                        {
                            filterContext.Controller.ViewData.Add("Limited" + item["OpCode"],
                                bool.Parse(item["RoleMark"].ToString()) ||
                                userManage.BeyondLimited(model.UserID, mapping.RoleID) ? "" : "display:none");
                        }
                    }
                }
            }
            else
            {
                filterContext.Result = new RedirectResult("~/Home/PurviewCheck");
            }
        }

    }

2.后台方法添加过滤器:(含新增和编辑是同一页面这种复杂的处理)

[OperationCheck(OpCode = "0401", PmKey = "isCreate", PmValue = "true")]
        [OperationCheck(OpCode = "0402", PmKey = "isCreate", PmValue = "false")]
        public ActionResult AddDomain(Guid? domainId, string isCreate)
{
//...
return View(orgDomain);
}
[HttpPost]
        [OperationCheck(OpCode = "0401", PmKey = "isCreate", PmValue = "true")]
        [OperationCheck(OpCode = "0402", PmKey = "isCreate", PmValue = "false")]
        public JsonResult Add(TSysDomain domain, string isCreate)
{
//...
return Json(new {Id=...});
}

3.前台连接页面事件,过滤器返回的是页面,前端不用处理

$("#btnShowNew").click(function () {
                var row = $('#DomainTable').treegrid('getSelections');
                if (row != null && row.length > 0) {
                    var domainID = "";
                    if (row.length > 1) {
                        alert(Language_Domain_EditOnlyCanSelectOneToCreateWarning);
                    }
                    else {
                        domainID = row[0].DomainID;
                        //var css = row[0].CSSFileName;
                        if (row[0].Enable == false) {
                            alert("该区域已冻结,不能在此域下新增!");
                        }
                        else {
                            var url = '@Url.Action("AddDomain", "Domain")' + "?isCreate=true" + "&domainId=" + domainID;
                            location.href = url;
                        }
                        //window.open(url);//这种方式有问题
                    }
                }
                else {
                    var url = '@Url.Action("AddDomain", "Domain")' + "?isCreate=true";
                    location.href = url;

                }
            })

功能按钮显示隐藏:

<div class="classdetail_btn_box01" style="@ViewData["Limited0401"]">
                  <div class="classdetail_btn_box0101">
                      @*<a href="@Url.Action("AddDomain", "Domain")"><span id="btnShowNew" style="@ViewData["Limited0401"]" class="classdetail_btn_box0102 domains_btn_box"></span></a>*@
                      <span id="btnShowNew" class="classdetail_btn_box0102 domains_btn_box"></span>
                  </div>
             </div>

4.在用Ajax提交事件中,要自行处理经过滤器过滤后的结果,

$.ajax({
                url: '@Url.Action("Add", "Domain")' + "?isCreate=" + '@Session["isCreate"]',
                type: "POST",
                async: false, //代表同步
                data: {
                    xDomainId: $("#DomainID").val(),
                    xParentId: $("#ParentID").val(),
                    xParentName: encodeURIComponent($("#ParentDomainName").val()),
                    xDomainName: encodeURIComponent($("#DomainName").val()),
。。。。。 }, success: function (result) { if (result == "0") { window.location.href = '@Url.Content("~/Home/PurviewCheck")' } else { if (result.DomainID != '@Guid.Empty') { window.location.href = '@Url.Content("~/Domain/AddDomain")' + "?domainId=" + result.DomainID + "
&isCreate=false"; } } }, error: function (result) { alert(result.message); } });

因为在Ajax中,执行过滤器后最终会跑到我们的success代码中,
过滤器执行后的结果就是result,此时result是html页面代码(含js代码),这个js代码不会自己执行的,要自己去处理。

即使把过滤器中filterContext.Result = new ContentResult { Content = ""<script type='text/javascript'>alert('权限验证不通过!');history.go(-1);</script>"" };它也不会去执行,Ajax这种提交不同于简单的页面URL连接,不管有没用进后台,如果没有权限,只要语法没错,它依然会执行success函数.

==>上面这段经同事验证是不正确的,用JS可以实现

不用走Ajax Success函数后面的代码(如果验证是没有权限的话)

在公用的JS重加入如下代码:

/*Ajax没有权限操作,跳转*/
$(function () {
    $("body").ajaxComplete(function (event, request, settings) {
        if (request.responseText == "NotAuthorized") {
            window.location.href = window.parent.document.URL + "/Home/PurviewCheck";
        }
    });
})

,所用上面的Success函数可以修改如下:

success: function (result) {
                    if (result.DomainID.length == 32) {
                        if (result.DomainID != '@Guid.Empty') {
                            window.location.href = '@Url.Content("~/Domain/AddDomain")' + "?domainId=" + result.DomainID + "&isCreate=false";
                        } 
                    }

                },
                error: function (result) {
                    alert(result.responseText);
                }

Fitler如下改:

filterContext.Result = new ContentResult { Content = "NotAuthorized" };

经验证,这样也是不对的,第一次可以 ,多试几次就多往下执行了。

于是,想到了ajax的几个事件的执行循序,

(参考园子里张子秋的博客:http://www.cnblogs.com/zhangziqiu/archive/2009/05/08/jQuery-Learn-6.html

 $("body").ajaxError(function (event, request, settings, errorThrown) (异常时执行)

 $("body").ajaxStart(function (event, request, settings, errorThrown)(AJAX 请求开始时执行函数)

 $("body").ajaxSendfunction (event, request, settings, errorThrown)(AJAX 请求发送前执行函数)

 $("body").ajaxSucess(function (event, request, settings, errorThrown)(AJAX 请求成功时执行函数)

 $("body").ajaxCompleted(function (event, request, settings, errorThrown)(AJAX 请求完成时执行函数)

 $("body").ajaxStop(function (event, request, settings, errorThrown)(AJAX 请求结束时执行函数)

结合我们的过滤器,执行循序如下:

ajax提交->

ajaxStart,

ajaxSend,

Fitler

ajaxSuccess

ajaxCompleted

ajaxStop

-->重新回到ajax 提交的success函数

如此一来的话,还是会执行success函数以下的代码,过滤器没有起到拦截的作用。

想到过滤器中设置错误代码,request.status,参考园子里:http://www.cnblogs.com/wintersun/archive/2011/12/18/2291800.html

JQuery的全局AjaxError配置,可参考官方

$("#errorh3info").ajaxError(function (event, request, settings) {

    $(this).append("<li>settings.url:" + settings.url + "</li>");

    $(this).append("<li>request.status:" + request.status + "</li>");

    $(this).append("<li>request.statusText:" + request.statusText + "</li>");

 

    //request.responseText

    if (request.responseText != "") {

        var jsonValue = jQuery.parseJSON(request.responseText);

        $(this).append("<li>ErrorMessage:" + jsonValue.ErrorMessage + "</li>");

    }

});

给button增加一个Post请求:

$('#inputajax1').click(function () {

    $.post('/Home/YouKnow/'

   , { id: 1 }

   , function (data) {

       $('div#right-box.data').html(data);

   });

});

这时View上将显示, 我们将看到ErrorMessage是JSON对象的属性:
settings.url:/Home/YouKnow/
request.status:500
request.statusText:error
ErrorMessage:Value cannot be null. Parameter name: YouKnow method: fc should not be null 

在过滤器设置500错误代码,进入ajaxError,如果真能进来,我就可以这样:

$("body").ajaxError(function (event, request, settings, errorThrown) {
        alert("ajaxError");
        if (request.responseText.indexOf("*|") > 0 && request.status == 500) {
            window.location.href = window.parent.document.URL + "/Home/PurviewCheck";
            return;
        }
    });

这样便实现拦截了,
可以有时,你设置了错误代码,它也不进入ajaxError,

郁闷中,求解!

原文地址:https://www.cnblogs.com/8090sns/p/Filter.html