MVC 自定义AuthorizeAttribute实现权限管理


ASP.NET MVC Framework支持四种不同类型的Filter:

  1. Authorization filters – 实现IAuthorizationFilter接口的属性.
  2. Action filters – 实现IActionFilter接口的属性.
  3. Result filters – 实现IResultFilter接口的属性.
  4. Exception filters – 实现IExceptionFilter接口的属性.

Filter的默认的执行顺序按上面的列表中顺序进行。如验证(authorization)Filter永远都是最开始执行的,异常(exception)Filter永远都是最后执行的,当然你也可以根据需要通过Order属性设定过滤器执行的顺序。

 

ASP.NET MVC Framework包括几种Action Filter:

 

名称 说明
OutputCacheAttribute  类似于 Web Form中在 OutputCache 指令。 OutputCache 属性允许在 MVC Framework 缓存控制器的输出。
ValidateInputAttribute 

类似于 Web Form中 ValidateRequest 属性。 MVC 框架默认将为 检查HTML 或其他危险输入传入的 HTTP 请求。 如果检测到,将引发异常。 使用此属性可以禁用请求验证。

AuthorizeAttribute Authorize 属性,可以对控制器操做的声明性的授权检查。 该属性可以限制特定角色中的用户的操作。 当您创建只应该给管理员角色中的用户的操作时,您可以使用此属性。默认使用的ASP.NET Membership服务,如果不使用ASP.NET 的Membership服务,可以继承AuthorizeAttribute,重写实现。
ValidateAntiForgeryTokenAttribute 此属性是一个解决方案以帮助防止跨站点的请求攻击 (CSRF)。 它允许验证的 HTTP POST 为特定于用户的标记在 Framework。 有关详细信息 CSRFs,请参阅" 使用 ASP.NET MVC AntiForgeryToken() 帮助器防止跨站点请求伪造 (CSFR)."

 

 

 

 

 

 

 

 

 

 

 

验证(authorization)filter用于实现在controller action上的验证和授权,如Authorize filter就是一个验证filter的例子;

Action filter包含一些逻辑,用于该action执行之前或者之后。比如可以使用一个action filter来修改action返回的view data;

Result filter包含一些逻辑,用于该action的view result执行之前和之后。比如可以修改一个view result在view被呈现到浏览器之前;

异常(Exception)Action用于处理异常信息,同样可以使用异常filter记录错误日志。

这些的filter类型是根据指定的顺序执行的,如果需要控制他们的执行顺序,需要设置filter的Order属性。

这些类的基类为System.Web.Mvc.FilterAttribute类,如果需要实现一个特定的filter类型,可以创建一个继承这个类的类,并且实现一个或多个IAuthorization、IActionFilter、IResultFilter、ExceptionFilter接口。

 

Action过滤器的作用范围

除了用Action过滤器标记一个Action方法外,你也可以用来标记一个完成的控制器类。如果这样的话,这个Action过滤器将会应用到该控制器的所有Action方法上。

另外,如果你的控制器类继承自别的控制器类,而基控制器类可能有它自己的Action过滤器Attributes。如果你在子类中重写了基控制器类的Action方法,则子类的该Action方法也会有它自己的从基类继承而来的Action过滤器Attributes。

 

Action过滤器的执行顺序

每一个Action过滤器都有一个 Order 属性,用来决定Action过滤器在该范围内的执行顺序。Order属性必需是0(默认值)或者更大的整数值。省略Order属性则会给该过滤器的Order值为 -1, 表明为指明顺序。任何一个在同一范围的Action过滤器Order设为 -1 的都将按不确定的顺序执行,单在此之前过滤器有一个特定的顺序(注:下面会说到).

当设置Order属性的值的时候,必需指定一个唯一的值。如果两个或者更多的Action过滤器具有相同的Order属性值,将会抛出一个异常。

来看一个示例:

[Filter1(Order = 2)]
[Filter2(Order 
= 3)]
[Filter3(Order 
= 1)]
public void Index()
{
    RenderView(
"Index");
}

Filter的执行顺序为:Filter3 => Filter1 => Filter2.

 

内置过滤器简介

1OutputCacheAttribute

 

[OutputCache(Duration=10,VaryByParam="none")]
        public ActionResult TestCache() {
            ViewBag.Time = DateTime.Now.ToString();
            return View();
        }

 

上述代码在动作方法上设置了OutputCache特性,使得页面具有缓存功能,缓存10秒。Cache的属性也可在web.config中设置

复制代码
<caching>
          <outputCacheSettings>
              <outputCacheProfiles>
                  <add name="outPutCache" duration="10" varyByParam="none"/>
              </outputCacheProfiles>
          </outputCacheSettings>
      </caching>
复制代码

相应的动作方法上

[OutputCache(CacheProfile="outPutCache")]
        public ActionResult TestCache() {
            ViewBag.Time = DateTime.Now.ToString();
            return View();
        }

2 AuthorizeAttribute

该类主要实现页面的成员和角色管理,AuthorizeAttribute类中设置了两个属性,分别是Users和Roles,分别表示成员和角色

[Authorize]
        public ActionResult TestAuthorize()
        { 
            return View();
        }

上述动作方法上设置了Authorize特性,指明该动作方法只能是注册并通过认证的用户才能访问

[Authorize(Users="test1,test2")]
        public ActionResult TestAuthorize()
        { 
            return View();
        }

上述动作方法指明只有用户test1,test2能够访问,如果用户过多,可以将这些用户设置为一类角色,通过设置角色参数来设定

  [Authorize(Roles="Admin")]
        public ActionResult TestAuthorize()
        { 
            return View();
        }

上述动作方法指明只有角色为Admin的用户才能访问。

3 HandleErrorAttribute

该类实现网站中指定控制器或者控制器中相关方法的异常处理。在动作方法上设置了[HandleError]特性后,发生异常时将自动交给异常处理页面,在MVC 4中,创建项目时,已经在Global.asax中添加了全局的HandleError特性

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

 

ActionFilterAttribute基类

为了让用户更简单的创建一个自定义action filter,ASP.NET MVC Framework提供了一个基类ActionFilterAttribute,这个类实现了IActionFilter和IResultFilter接口,并且继承了Filter类。

这里的术语并不完全一致,从技术上说,这个类继承ActionFitlerAttribute,并且同时实现了action filter和result filter接口,但是从宽松意义上说,在ASP.NET MVC Framework中,任何实现filter的类型都是action filter。

ActionFilterAttribute类有以下的方法可以重写:

  • OnActionExecuting – 在controller action执行之前调用
  • OnActionExecuted – 在controller action执行之后调用
  • OnResultExecuting – 在controller action result执行之前调用
  • OnResultExecuted – 在controller action result执行之后调用

以下是这几个方法的执行顺序:

OnActionExecuting()-> Action execute & return View() ->OnActionExecuted() ->OnResultExecuting() -> Render View() ->OnResultExecuted()

为了阐述如何创建一个自定义的action filter,我们会创建一个自定义的action filter,用于记录controller action处理的不同阶段的日志信息,并显示到Visual Studio输出窗口中。

 

复制代码
 public class LoginActionFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            Log("OnActionExecuted",filterContext.RouteData);
            base.OnActionExecuted(filterContext);
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            Log("OnActionExecuting", filterContext.RouteData);
            base.OnActionExecuting(filterContext);
        }
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            Log("OnResultExecuted", filterContext.RouteData);
            base.OnResultExecuted(filterContext);
        }
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            Log("OnResultExecuting", filterContext.RouteData);
            base.OnResultExecuting(filterContext);
        }
        private void Log(string methodName, RouteData routeData) {
            var controllerName = routeData.Values["controller"];
            var actionName = routeData.Values["action"];
            var message = String.Format("{0} controller: {1} action: {2}", methodName, controllerName, actionName);
            Debug.WriteLine(message,"Action Filter Log");
        }
    }
复制代码

 

输出结果:

Action Filter Log: OnActionExecuting controller: Home action: Index
Action Filter Log: OnActionExecuted controller: Home action: Index
Action Filter Log: OnResultExecuting controller: Home action: Index
Action Filter Log: OnResultExecuted controller: Home action: Index




--------------------------------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

在上一节中提到可以使用AuthorizeAttribute进行权限管理:

复制代码
        [Authorize]
        public ActionResult TestAuthorize()
        { 
            return View();
        }

        [Authorize(Users="test1,test2")]
        public ActionResult TestAuthorize()
        { 
            return View();
        }

        [Authorize(Roles="Admin")]
        public ActionResult TestAuthorize()
        { 
            return View();
        }
复制代码

但是通常情况下,网站的权限并不是固定不变的,当新增角色或者角色改变时,只能修改每个Action对应的特性,当项目较大时工作量可想而知。幸运的是我们可以重写AuthorizeAttribute达到自定义的权限管理。新建一个CustomAuthorizeAttribute类,使这个类继承于AuthorizeAttribute。打开AuthorizeAttribute查看下方法说明,我们只需要重写AuthorizeCore和OnAuthorization就能达到我们的目的。

 

复制代码
// Summary:
        //     When overridden, provides an entry point for custom authorization checks.
        //
        // Parameters:
        //   httpContext:
        //     The HTTP context, which encapsulates all HTTP-specific information about
        //     an individual HTTP request.
        //
        // Returns:
        //     true if the user is authorized; otherwise, false.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     The httpContext parameter is null.
        protected virtual bool AuthorizeCore(HttpContextBase httpContext);


//
        // Summary:
        //     Called when a process requests authorization.
        //
        // Parameters:
        //   filterContext:
        //     The filter context, which encapsulates information for using System.Web.Mvc.AuthorizeAttribute.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     The filterContext parameter is null.
        public virtual void OnAuthorization(AuthorizationContext filterContext);
复制代码

 

 

CustomAuthorizeAttribute重载AuthorizeCore方法,它的处理逻辑如下:首先判断当前账户是否被认证,如果没有,则返回false;然后获取当前账户的类型,并跟给定的类型进行比较,如果类型相同,则返回true,否则返回false。一般网站中权限管理都会使用权限树,然后将角色的权限保存至数据库或者文件中,本例中我们使用XML文件保存每个Action的角色,这样在用户请求Action时,由XML文件获取Action对应的权限,然后检测账户是否有相应的权限。CustomAuthorizeAttribute类的代码如下:

 

复制代码
public class CustomAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
    {
        public new string[] Roles { get; set; }
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null) {
                throw new ArgumentNullException("HttpContext");
            }
            if (!httpContext.User.Identity.IsAuthenticated) {
                return false;
            }
            if (Roles == null) {
                return true;
            }
            if (Roles.Length == 0)
            {
                return true;
            }
            if (Roles.Any(httpContext.User.IsInRole))
            {
                return true;
            }
            return false;
        }

        public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            string actionName = filterContext.ActionDescriptor.ActionName;
            string roles = GetRoles.GetActionRoles(actionName, controllerName);
            if (!string.IsNullOrWhiteSpace(roles)) {
                this.Roles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }
            base.OnAuthorization(filterContext);
        }
    }
复制代码

 

当用户请求一个Action时,会调用OnAuthorization方法,该方法中GetRoles.GetActionRoles(actionName, controllerName);根据Controller和Action去查找当前Action需要具有的角色类型,获得Action的Roles以后,在AuthorizeCore中与用户的角色进行比对Roles.Any(httpContext.User.IsInRole),如果没有相应权限则返回false,程序就会自动跳转到登录页面

 

GetRoles为XML解析类,代码如下:

 
  
复制代码
   public class GetRoles
    {
       
        public static string GetActionRoles(string action, string controller) {
            XElement rootElement = XElement.Load(HttpContext.Current.Server.MapPath("/")+"ActionRoles.xml");
            XElement controllerElement = findElementByAttribute(rootElement, "Controller", controller);
            if (controllerElement != null)
            {
                XElement actionElement = findElementByAttribute(controllerElement, "Action", action);
                if (actionElement != null)
                {
                    return actionElement.Value;
                }
            }
            return "";
        }

        public static XElement findElementByAttribute(XElement xElement,string tagName, string attribute)
        {
            return xElement.Elements(tagName).FirstOrDefault(x => x.Attribute("name").Value.Equals(attribute,StringComparison.OrdinalIgnoreCase));
        }
    }
复制代码

 

相应的权限XMl文件:

 

复制代码
<?xml version="1.0" encoding="utf-8" ?>
<Roles>
    <Controller name="Home">
        <Action name="Index"></Action>
        <Action name="About">Manager,Admin</Action>
        <Action name="Contact">Admin</Action>
    </Controller>
</Roles>
复制代码

 

当需求发生变化时,只需要修改XML文件即可

使用时,只需要在FilterConfig中注册该filter

filters.Add(new CustomAuthorizeAttribute());

 

当然这只是一个简单的例子,实际应用中会复杂许多,还可能要实现在即的MemberShipProvider和RoleProvider

--------------------------------------------------------------------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值