认识ASP.NET MVC的5种AuthorizationFilter

在总体介绍了筛选器及其提供机制(《深入探讨ASP.NET MVC的筛选器》)之后,我们按照执行的先后顺序对四种不同的筛选器进行单独介绍,首先来介绍最先执行的AuthorizationFilter。从命名 来看,AuthorizationFilter用于完成授权相关的工作,所以它应该在Action方法被调用之前执行才能起到授权的作用。不仅限于授权, 如果我们希望目标Action方法被调用之前中断执行的流程“做点什么”,都可以以AuthorizationFilter的形式来实现。

一、IAuthorizationFilter

所有的AuthorizationFilter实现了接口IAuthorizationFilter。如下面的代码片断所示,IAuthorizationFilter定义了一个OnAuthorization方法用于实现授权的操作。作为该方法的参数filterContext是一个表示授权上下文的AuthorizationContext对象, 而AuthorizationContext直接继承自ControllerContext。

双击代码全选

1

1:publicinterface IAuthorizationFilter

 

双击代码全选

1

2: {

 

双击代码全选

1

3:void OnAuthorization(AuthorizationContext filterContext);

 

双击代码全选

1

4: }

 

双击代码全选

1

 

 

双击代码全选

1

6:publicclass AuthorizationContext : ControllerContext

 

双击代码全选

1

7: {

 

双击代码全选

1

8:public AuthorizationContext();

 

双击代码全选

1

9:public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor);

 

双击代码全选

1

10:

 

双击代码全选

1

11:publicvirtual ActionDescriptor ActionDescriptor { get; set; }

 

双击代码全选

1

12:public ActionResult Result { get; set; }

 

双击代码全选

1

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

1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]

 

双击代码全选

1

2:publicclass AuthorizeAttribute : FilterAttribute, IAuthorizationFilter

 

双击代码全选

1

3: {

 

双击代码全选

1

4://其他成员

 

双击代码全选

1

5:publicvirtualvoid OnAuthorization(AuthorizationContext filterContext);

 

双击代码全选

1

6:protectedvirtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);

 

双击代码全选

1

 

 

双击代码全选

1

8:publicstring Roles { get; set; }

 

双击代码全选

1

9:publicoverrideobject TypeId { get; }

 

双击代码全选

1

10:publicstring Users { get; set; }

 

双击代码全选

1

11: }

 

如果授权失败(当前访问者是未被授权用户,或者当前用户的用户名或者角色没有在指定的授权用户或者角色列表中),AuthorizeAttribute会创建一个HttpUnauthorizedResult对象,并赋值给AuthorizationContext的Result属性,意味着会响应一个状态为“401,Unauthorized”的回复。如果采用Forms认证,配置的登录页面会自动被显示。
很 多会将AuthorizeAttribute对方法的授权与PrincipalPermissionAttribute等同起来,实际上不但它们实现授权 的机制不一样(后者是通过代码访问安全检验实现对方法调用的授权),它们的授权策略也一样。以下面定义的两个方法为例,应用了 PrincipalPermissionAttribute的FooOrAdmin意味着可以被帐号为Foo或者具有Admin角色的用户访问,而应用了 AuthorizeAttribute特性的方法FooAndAdmin方法则只能被用户Foo访问,而且该用户必须具有Admin角色。也就是说 PrincipalPermissionAttribute特性对User和Role的授权逻辑是“逻辑或”,而AuthorizeAttribute 采用的则是“逻辑与”。

双击代码全选

1

1: [PrincipalPermission( SecurityAction.Demand,Name="Foo", Role="Admin")]

 

双击代码全选

1

2:publicvoid FooOrAdmin()

 

双击代码全选

1

3: { }

 

双击代码全选

1

 

 

双击代码全选

1

5: [Authorize(Users="Foo", Roles="Admin")]

 

双击代码全选

1

6:publicvoid FooAndAdmin()

 

双击代码全选

1

7: { }

 

除 此之外,我们可以将多个PrincipalPermissionAttribute和AuthorizeAttribute应用到同一个类型或者方法上。 对于前者,如果当前用于通过了任意一个PrincipalPermissionAttribute特性的授权就有权调用目标方法;对于后者来说,意味着需 要通过所有AuthorizeAttribute特性的授权在具有了调用目标方法的权限。以如下两个方法为例,用户Foo或者Bar可以有权限调用 FooOrBar方法,但是没有任何一个用户有权调用CannotCall方法(因为一个用户只一个用户名)。

双击代码全选

1

1: [PrincipalPermission( SecurityAction.Demand, Name="Foo")

 

双击代码全选

1

2: [PrincipalPermission( SecurityAction.Demand, Name="Bar")]

 

双击代码全选

1

3:publicvoid FooOrBar()

 

双击代码全选

1

4: { }

 

双击代码全选

1

 

 

双击代码全选

1

6: [Authorize(Users="Foo")]

 

双击代码全选

1

7: [Authorize(Users="Bar")]

 

双击代码全选

1

8:publicvoid CannotCall()

 

双击代码全选

1

9: {}