认识ASP.NET MVC的5种AuthorizationFilter2012-12-03蒋金楠在总体介绍了筛选器及其提供机制(《深入探讨ASP.NET MVC的筛选器》)之后,我们按照执行的先后顺序对四种不同的筛选器进行单独介绍,首先来介绍最先执行的AuthorizationFilter。从命名来看,AuthorizationFilter用于完成授权相关的工作,所以它应该在Action方法被调用之前执行才能起到授权的作用。不仅限于授权,如果我们希望目标Action方法被调用之前中断执行的流程“做点什么”,都可以以AuthorizationFilter的形式来实现。一、IAuthorizationFilter所有的AuthorizationFilter实现了接口IAuthorizationFilter。如下面的代码片断所示,IAuthorizationFilter定义了一个OnAuthorization方法用于实现授权的操作。作为该方法的参数filterContext是一个表示授权上下文的AuthorizationContext对象, 而AuthorizationContext直接继承自ControllerContext。
 1: public interface IAuthorizationFilter
 2: {
 3: void OnAuthorization(AuthorizationContext filterContext);
 4: }
 5:
 6: public class AuthorizationContext : ControllerContext
 7: {
 8: public AuthorizationContext();
 9: public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
10:
11: public virtual ActionDescriptor ActionDescriptor { get; set; }
12: public ActionResult Result { get; set; }
13: }
AuthorizationContext的ActionDescriptor属性表示描述当前执行Action的ActionDescriptor对象,而Result属性返回一个用于在授权阶段呈现的ActionResult。AuthorizationFilter的执行是ActionInvoker进行Action执行的第一项工作,因为后续的工作(Model绑定、Model验证、Action方法执行等)只有在成功授权的基础上才会有意义。ActionInvoker在通过执行AuthorizationFilter之前,会先根据当前的Controller上下文和解析出来的用于描述当前Action的ActionDescriptor,并以此创建一个表示授权上下文的AuthorizationContext对象。然后将此AuthorizationContext对象作为参数,按照Filter对象Order和Scope属性决定的顺序执行所有AuthorizationFilter的OnAuthorization。在所有的AuthorizationFilter都执行完毕之后,如果指定的AuthorizationContext对象的Result属性表示得ActionResult不为Null,整个Action的执行将会终止,而ActionInvoker将会直接执行该ActionResult。一般来说,某个AuthorizationFilter在对当前请求实施授权的时候,如果授权失败它可以通过设置传入的AuthorizationContext对象的Result属性响应一个“401,Unauthrized”回复,或者呈现一个错误页面。二、AuthorizeAttribute如果我们要求某个Action只能被认证的用户访问,可以在Controller类型或者Action方法上应用具有如下定义的AuthorizeAttribute特性。AuthorizeAttribute还可以具体限制目标Action可被访问的用户或者角色,它的Users和Roles属性用于指定被授权的用户名和角色列表,中间用采用逗号作为分隔符。如果没有显式地对Users和Roles属性进行设置,AuthorizeAttribute在进行授权操作的时候只要求访问者是被认证的用户。
 1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
 2: public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
 3: {
 4: //其他成员
 5: public virtual void OnAuthorization(AuthorizationContext filterContext);
 6: protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);
 7:
 8: public string Roles { get; set; }
 9: public override object TypeId { get; }
10: public string Users { get; set; }
11: }
如果授权失败(当前访问者是未被授权用户,或者当前用户的用户名或者角色没有在指定的授权用户或者角色列表中),AuthorizeAttribute会创建一个HttpUnauthorizedResult对象,并赋值给AuthorizationContext的Result属性,意味着会响应一个状态为“401,Unauthorized”的回复。如果采用Forms认证,配置的登录页面会自动被显示。很多会将AuthorizeAttribute对方法的授权与PrincipalPermissionAttribute等同起来,实际上不但它们实现授权的机制不一样(后者是通过代码访问安全检验实现对方法调用的授权),它们的授权策略也一样。以下面定义的两个方法为例,应用了PrincipalPermissionAttribute的FooOrAdmin意味着可以被帐号为Foo或者具有Admin角色的用户访问,而应用了AuthorizeAttribute特性的方法FooAndAdmin方法则只能被用户Foo访问,而且该用户必须具有Admin角色。也就是说PrincipalPermissionAttribute特性对User和Role的授权逻辑是“逻辑或”,而AuthorizeAttribute 采用的则是“逻辑与”。
 1: [PrincipalPermission( SecurityAction.Demand,Name="Foo", Role="Admin")]
 2: public void FooOrAdmin()
 3: { }
 4:
 5: [Authorize(Users="Foo", Roles="Admin")]
 6: public void FooAndAdmin()
 7: { }
除此之外,我们可以将多个PrincipalPermissionAttribute和AuthorizeAttribute应用到同一个类型或者方法上。对于前者,如果当前用于通过了任意一个PrincipalPermissionAttribute特性的授权就有权调用目标方法;对于后者来说,意味着需要通过所有AuthorizeAttribute特性的授权在具有了调用目标方法的权限。以如下两个方法为例,用户Foo或者Bar可以有权限调用FooOrBar方法,但是没有任何一个用户有权调用CannotCall方法(因为一个用户只一个用户名)。
 1: [PrincipalPermission( SecurityAction.Demand, Name="Foo")
 2: [PrincipalPermission( SecurityAction.Demand, Name="Bar")]
 3: public void FooOrBar()
 4: { }
 5:
 6: [Authorize(Users="Foo")]
 7: [Authorize(Users="Bar")]
 8: public void CannotCall()
 9: {}