C# MVC权限验证

前言

之前一直没怎么接触过权限验证这块,刚好公司老平台改版,就有了这篇权限验证。此篇文章大致讲解下 精确到按钮级别的验证如何实现、以及权限验证设计的参考思路(菜鸟一枚,大神勿喷)。

在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发人员去关心和写类似身份验证,日志,异常,行为截取等这部分重复的代码,那我们可以通过AOP截取实现,而在MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复杂的AOP了。

在Asp.NET Mvc中当你有以下及类似以下需求时你可以使用Filter(过滤器)功能

  1. 判断登录与否或用户权限
  2. 决策输出缓存
  3. 防盗链
  4. 防蜘蛛
  5. 本地化与国际化设置
  6. 实现动态Action

第一节:知识储备

  1. Asp.Net MVC提供了以下几种默认的Filter:


  2. 大家注意一点,Asp.Net MVC提供的ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我们想使用它必须继承一下它然后才能使用。

    Filter继承于ActionFilterAttribute抽象类,并可以覆写                                                                                                                                                                 void OnActionExecuting(ActionExecutingContext)                                                                                                                                      voidOnActionExecuted(ActionExecutedContext)                                                                                                                                                void OnResultExecuting(ResultExecutingContext)                                                                                                                                                          void OnResultExecuted(ResultExecutedContext)。

  3. 它们的执行先后顺序如下:

      OnActionExecuting是Action执行前的操作

      OnActionExecuted则是Action执行后的操作

      OnResultExecuting是解析ActionResult前执行

      OnResultExecuted是解析ActionResult后执行

    接下来我们只要对以上的方法进行重写就可以在相应的步骤做一些操作了。

第二节:Filter实战

光说不练假把式,下面我给大家一个示例,来看看它们的执行顺序,首先添加一个普通的类TestFilterAttribute,这个类要继承ActionFilterAttribute,代码如下:

 1     /// <summary>
 2     /// Filter 执行顺序Test,需要继承筛选器的基类ActionFilterAttribute
 3     /// </summary>
 4     public class TestFilterAttribute : ActionFilterAttribute
 5     {
 6         public string Message { get; set; }
 7
 8         public override void OnActionExecuting(ActionExecutingContext filterContext)
 9         {
10             base.OnActionExecuting(filterContext);
11             filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
12         }
13
14         public override void OnActionExecuted(ActionExecutedContext filterContext)
15         {
16             base.OnActionExecuted(filterContext);
17             filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
18         }
19
20         public override void OnResultExecuting(ResultExecutingContext filterContext)
21         {
22             base.OnResultExecuting(filterContext);
23             filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
24         }
25
26         public override void OnResultExecuted(ResultExecutedContext filterContext)
27         {
28             base.OnResultExecuted(filterContext);
29             filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
30         }
31     }

写完这个代码后,我们回到Action上,打上上面的标记如下所示:

1        [TestFilterAttribute(Message = "Action")]
2         public ActionResult Index()
3         {
4             HttpContext.Response.Write("Action正在执行···<br />");
5             return Content("正在返回Result···<br />");
6         }

然后通过浏览器访问上面的Action便可以看到下面的执行顺序

总的执行顺序是:

Action执行前:OnActionExecuting方法先执行→Action执行→OnActionExecuted方法执行→OnResultExecuting方法执行→返回的ActionRsult中的executeResult方法执行→OnResultExecuted执行

最终显示的效果就是如上图所示。

感觉还不错吧!哈哈!这要想用到这个过滤机制的地方的时候,只要在Action上面添加标记便可以实现效果。

如果我们将此标签打到Controller上的话,TestFilterAttributeFilter将作用到Controller下的所有的Action。例如如下代码所示:

    [TestFilter(Message="Controller")]
    public class TestFilterController : Controller
    {
        //
        // GET: /TestFilter/
        [TestFilter(Message="Action")]
        public ActionResult Index()
        {
            HttpContext.Response.Write("Action正在执行···<br />");
            return Content("正在返回Result···<br />");
        }

    }

如果单纯的按照上面的代码来做就有个问题了,我们再执行显示的页面会有什么情况呢?Controller上的Filter会执行吗?那标签的作用会执行两次吗?下面是最后的执行结果如下图所示:

结果说明

默认情况下Action上打了TestFilterAttribute 标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起作用了。

补充:如果Action没有打上TestFilterAttribute标签,那么Controller上的标签便会被执行。

Index 执行时,Filter的方法只执行了一次,而某些情况下我们也想让Controller上的FilterAttribute也执行一次TestFilterAttribute,那我们怎么才能让Controller上的[TestFilter(Message = "controller")]也起作用呢?

答案是:我们只需在TestFilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面类的最上面红色字体部分】,也就是让其成为可以多次执行的Action代码如下:

/// <summary>
    /// Filter 执行顺序Test,需要继承筛选器的基类ActionFilterAttribute
    /// </summary
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] //打标记是让所有的Controller的标记生效
    public class TestFilterAttribute : ActionFilterAttribute
    {
        public string Message { get; set; }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
            filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);
            filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
            filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
        }
    }

浏览效果如下图:

我们看到的结果是Controller上的ActionFilter先于Action上打的标记执行。同样Result执行executeResult方法之前也是先执行Controller上的Filter标记中的OnResultexecuteing方法。

最后的执行顺序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action执行→Action上的OnActionExecuted→Controller上的OnActionExecuted

到此Action就执行完毕了,我们看到是一个入栈出栈的顺序。后面是Action返回ActionResult后执行了ExecuteResult方法,但在执行之前要执行Filter。具体顺序为:

接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回ActionResult后执行了ExecuteResult方法→Action上的OnResultExecuted执行→Controller上的OnResultExecuted执行→结束。

又接着一个问题也来了,我们想有些公共的方法需要每个Action都执行以下,而在所有的Controller打标记是很痛苦的。幸好Asp。Net MVC3带来了一个美好的东西,全局Filter。而怎么注册全局Filter呢?答案就在Global.asax中。让我们看以下代码,我是如何将上面我们定义的TestFilterAttribute 注册到全局Filter中。上代码:

      public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            //注册全局过滤器
            filters.Add(new TestFilterAttribute() { Message="全局"});
        }

效果如下图:

我们看到的结果是全局的Action首先执行,然后才是Controller下的Filter执行,最后才是Action上的标签执行。当然这是在TestFilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果Action打上了标签跟Controller的相同则它只会执行Action上的Filter。

第三节:其他Filter

AcceptVerbs

规定页面的访问形式,如

        [AcceptVerbs(HttpVerbs.Post)]
         public ActionResult Example(){
             return View();
         }

页面只能以Post形式访问,即表单提交。

ActionName

规定Action的名称。

应用场景:如果不想用方法名做为Action名,或Action名为关键字的话,如

       [ActionName("class")]
         public ActionResult Example(){
             return View();
         }

NonAction

当前方法仅是普通方法不解析为Action

OutputCache

为Action添加缓存

        [OutputCache(Duration = 60, VaryByParam = "*")]
        public ActionResult Example()
        {
            return View();
        }

ValidateInput

该Action可以接受Html等危险代码(ASP.NET MVC在aspx中设置<%@ Page 的属性无法完成等同任务。)

        [ValidateInput(false)]
        public ActionResult Example()
        {
            return View();
        }

ValidateAntiForgeryTokenAttribute

用于验证服务器篡改。

        [ValidateAntiForgeryToken]
        public ActionResult Example()
        {
            return View();
        }

Filter总结

经过这一篇文章的介绍我们大体了解了Filter的使用方法,还了解到全局Filter的用法,尤其是当相同的Filter重复作用到同一个Action上时,如果没有设置可多次执行的标签那只有Action上的Filter执行,而Controller和全局Filter都被屏蔽掉,但是设置可多次执行,那首先执行全局Filter其次是Controller再次之就是Action上的Filter了。同时还了解了系统的Filter的用法。

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

                  万恶的分割线

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

权限设计

我们可以先来看看它们之间的关系

可能大家看这个数据字典还不够直观那我们来看看下图描述的:

总体上分为:组织权限管理、管理员管理、功能菜单管理、管理组、菜单动作管理几部分。

注意:因为组和角色都具有上下级关系,所以下级的组或角色的权限只能在自己的直属上级的权限中选择,下级的组或者角色的总的权限都不能大于直属上级的总权限。

菜单和按钮权限

新建权限实体

 public class UsersRolesOper
    {
        /// <summary>
        /// 权限ID
        /// </summary>
        public int RoleID { get; set; }
        /// <summary>
        /// 菜单名称
        /// </summary>
        public string MenuName { get; set; }
        /// <summary>
        /// 菜单code
        /// </summary>
        public string MenuCode { get; set; }
        /// <summary>
        /// 菜单ID
        /// </summary>
        public int MenuID { get; set; }
        /// <summary>
        /// 动作ID
        /// </summary>
        public int ActionID { get; set; }
        /// <summary>
        /// 动作名称
        /// </summary>
        public string ActionName { get; set; }
        /// <summary>
        /// 动作Code
        /// </summary>
        public string ActionCode { get; set; }
    }

新建过滤器类AuthorityCheck 实现权限验证

public class AuthorityCheck : ActionFilterAttribute
    {
        private readonly IRolesService _roles;public string KeyWork { get; set; }
        public string MoudleName { get; set; }
        public bool Menu_button { get; set; }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //之前执行
            HttpResponseBase response = filterContext.HttpContext.Response;
            if (_roles.AuthorityCheck(KeyWork, HttpContext.Current.User.Identity.Name, MoudleName,Menu_button))
            {
                filterContext.Controller.ViewBag.isPersimion = true;
            }
            else
            {
                filterContext.Controller.ViewBag.isPersimion = false;
            }
            base.OnActionExecuting(filterContext);
        }
    }
        /// <summary>
        /// 验证权限
        /// </summary>
        /// <param name="keyWork">传入code</param>
        /// <param name="userName">用户</param>
        /// <param name="ModuleName">菜单或按钮名称</param>
        /// <param name="Menu_button">菜单还是按钮,false是菜单</param>
        /// <returns></returns>
        public bool AuthorityCheck(string keyWork, string userName, string ModuleName,bool Menu_button=false)
        {
            List<UsersRolesOper> userRoleOper = GetUserPermission(userName, ModuleName);
            if (userRoleOper != null)
            {
                foreach (UsersRolesOper item in userRoleOper)
                {
                    if (Menu_button)
                    {
                        if (item.ActionCode.ToLower() == keyWork.ToLower())
                        {
                            return true;
                        }
                    }
                    else
                    {
                        if (item.ActionName == "查看")
                        {
                            if (item.MenuCode.ToLower() == keyWork.ToLower())
                            {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
            else
            {
                return false;
            }
        }

GetUserPermission方法就不贴了,主要是从数据库读取用户权限,存放在List集合中

Action使用

如果Action是返回的Json,可以返回Json后再前台处理,其中ViewBag.isPersimion对应过滤器类中返回的结果。例如:

        /// <summary>
        /// 编辑用户保存所有资料
        /// </summary>
        /// <param name="model"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [HttpPost]
        [AuthorityCheck(KeyWork = "Modify", MoudleName = "修改",Menu_button=true)]
        public ActionResult AccountEdit(GoldEditeView model, int type = 0)
        {
            ResponseData res = new ResponseData { Status = ResponseStatu.ERROR, Message = "保存出错" };
            if (!LoginInfo.IsSA)
            {
                if (!ViewBag.isPersimion)
                {
                    res = new ResponseData { Status = ResponseStatu.LIMIT, Message = "你没有权限进行此操作!" };
                    return MyJson(res);
                }
            }
            try
            {
                bool flag = false;
                if (model.Person.ID == 0)
                { }
                else
                {
                    model.PersonInfo.ModifiedBy = LoginInfo.UserName;
                    flag = _accService.UpdateGoldPerson(model);
                }
                if (flag)
                    res = new ResponseData { Status = ResponseStatu.SUCCESS, Message = "SUCCESS", Data = model.Person.ID };

            }
            catch (Exception ex)
            {

                res = new ResponseData { Status = ResponseStatu.SUCCESS, Message = "保存出错", Data = ex.Message };
            }
            return MyJson(res);
        }

如果是window.location.href方式的Action,可以这样处理:

 [HttpPost]
        [AuthorityCheck(KeyWork = "Export", MoudleName = "导出",Menu_button=true)]
        public ActionResult DoneExport(DoneDataSearchView entity = null)
        {
            if (!ViewBag.isPersimion)
            {
                return Content("<script>alert(‘您没有操作权限‘);location.href=‘/GoldAccounts/DoneList‘</script>");
            }
            try
            {
                byte[] file = this._accService.GetDoneExportDT(entity);
                if (file == null)
                    return Redirect("/GoldAccounts/DoneList");
                else
                    return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "GoldAccounts_Excel" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xls");
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }

最后,如果菜单没有权限,可以直接跳转到另一个提示页面:

 [AuthorityCheck(KeyWork = "paper_account", MoudleName = "开户受理", Menu_button = false)]
        public ActionResult PendingList(string id)
        {
            try
            {
                if (!ViewBag.isPersimion)
                {
                   return Redirect("/Home/Limit");
                }
                return View();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

最后附一张效果图:

时间: 2024-10-19 14:55:48

C# MVC权限验证的相关文章

MVC权限验证过滤器

Action属性,权限设定属性 [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class PurviewEnumAttribute : Attribute { public string PurviewString { get; set; } public PurviewEnumAttribute(string purview) { this.PurviewStr

MVC权限验证之ActionFilterAttribute

参考:http://www.cnblogs.com/waitingfor/archive/2011/12/27/2303784.html ActionFilterAttribute是Action过滤类,该属于会在执行一个action之前先执行. 而ActionFilterAttribute是 MVC的一个专门处理action过滤的类.基于这个原理 我们做一个权限限制 例如:如何访问 HomeController  里的test  action using System;using System.

mvc权限验证--AuthorizeAttribute

在做后台管理时用户登录后就需要验证哪些权限了,没有登录的就直接退出到登录页面. 系统有自带的权限[Authorize],可用于几个地方: 1.将属性[Authorize]置于相关的action上方,验证当前 [Authorize] public ActionResult Index() { return View(); } 2.将属性[Authorize]置于控制器上方 [Authorize] public class MainController : Controller { // } 3.将

【3】.net MVC 使用IPrincipal进行Form登录即权限验证

1.在MVC项目中添加用户类,可以根据实际项目需求添加必要属性 public class UserData { /// <summary> /// ID /// </summary> public int UserId { get; set; } /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 角色ID列表 //

MVC身份验证及权限管理

MVC自带的ActionFilter 在Asp.Net WebForm的中要做到身份认证微软为我们提供了三种方式,其中最常用的就是我们的Form认证,需要配置相应的信息.例如下面的配置信息: <authentication mode="Forms"> <forms loginUrl="Login.aspx" defaultUrl="Default.aspx" protection="All" /> &l

Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直接忽略,用户登录成功后怎么保存当前用户登录信息(session,cookie),本文介绍的是身份验证(其实就是基于cookie)的,下面看看代码. 引入命名空间 using System.Web.Security; Users ModelUser = new Users() { ID = 10000, Nam

ASP.NET MVC View 和 Web API 的基本权限验证

ASP.NET MVC 5.0已经发布一段时间了,适应了一段时间,准备把原来的MVC项目重构了一遍,先把基本权限验证这块记录一下. 环境:Windows 7 Professional SP1 + Microsoft Visual Studio 2013(MVC 5 + Web API 2) 修改Web.config,增加Forms验证模式,在system.web节点中增加以下配置: <authentication mode="Forms"> <forms loginU

Spring MVC + Shiro 实现权限验证

MAVEN的pom.xml 引入shiro(Spring MVC+mybatis 请参见上一章). <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version> </dependency> <!-- http://mvnrepository.co

在.Net MVC结构API接口中判断http头信息实现公共的权限验证过滤器示例

//control   action public class TestController : ApiController { [MyAuthFilter] public string test(string str) { return str.Trim(); } } //过虑器类 public class MyAuthFilter : ActionFilterAttribute { const string SecurityKeyName = "MySecurityKey";//h