ControllerActionInvoker在执行过程中除了利用ActionDescriptor完成对目标Action方法本身的执行外,还会执行相关过滤器(Filter)。过滤器采用AOP的设计,它使我们可以将一些非业务的逻辑在相应的过滤器中实现,并以一种横切的方式应用到对应的Action方法上。

根据用途和执行时机的不同,ASP.NET MVC提供的过滤器可分为6种

  1. AuthenticationFilter
  2. AuthorizationFilter
  3. ActionFilter
  4. ExceptionFilter
  5. ResultFilter
  6. OverrideFilter  :mvc5新增的一种特殊过滤器,它用于屏蔽应用在外围的过滤器。

主要对象

Filter

一个Filter对象就是对一个过滤器的封装。

1. Instance属性获取被封装的过滤器对象,Order决定过滤器的执行优先级,Scope决定应用范围。

2. 多个同类过滤器的执行顺序 :先比较Order,值较小的先执行,Order相同则比对Scope ,Scope的执行顺序为First->Global->Controller->Action->Last

public enum FilterScope
{
//
// 摘要:
// Specifies first.
First = 0,
//
// 摘要:
// Specifies an order before System.Web.Mvc.FilterScope.Controller and after System.Web.Mvc.FilterScope.First.
Global = 10,
//
// 摘要:
// Specifies an order before System.Web.Mvc.FilterScope.Action and after System.Web.Mvc.FilterScope.Global.
Controller = 20,
//
// 摘要:
// Specifies an order before System.Web.Mvc.FilterScope.Last and after System.Web.Mvc.FilterScope.Controller.
Action = 30,
//
// 摘要:
// Specifies last.
Last = 100
}

IFilterProvider

用于获取Filer对象。有三种原生的FilterProvider:

FilterAttributeFilterProvider

用于获取通过特性注册的过滤器

ControllerInstanceFilterProvider

Controller本身就是一个过滤器,ControllerInstanceFilterProvider获取相应的Controller对象,并以此创建Filter对象。这个过滤器的优先级最高,会被最先执行。

GlobalFilterCollection

用于注册全局性的过滤器,并实现了IFilterProvider接口。

FilterProviders

用于提供Filter的FilterProvider通过静态类型FilterProviders注册。

FilterInfo

当ControllerActionInvoker被调用的时候,它会利用静态类型FilterProviders得到所有注册的FilterProvider,并利用它们根据当前的ControllerContext和ActionDescriptor对象得到所有的Filter。然后根据其Instance属性表示的过滤器类型将它们分组,最后得到一个具有如下定义的FilterInfo对象。

public class FilterInfo
{
public FilterInfo(); public FilterInfo(IEnumerable<Filter> filters); public IList<IActionFilter> ActionFilters { get; } public IList<IAuthenticationFilter> AuthenticationFilters { get; } public IList<IAuthorizationFilter> AuthorizationFilters { get; } public IList<IExceptionFilter> ExceptionFilters { get; } public IList<IResultFilter> ResultFilters { get; }
}

  

ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));
ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "DemoAction");
IEnumerable<Filter> filters = FilterProviders.Providers.GetFilters(ControllerContext, actionDescriptor);

ps: 当以不同的Scope注册了多个AllowMultiple属性为false的某FilterAttribute类型的过滤器时,最终有效的是依执行顺序排在最后的那一个。

AuthenticationFilter

AuthenticationFilter用于在目标Action方法执行之前实施身份认证。采用的是“质询——应答(Chanllenge - Response)”的形式。认证方发出质询要被认证方提供用户凭证,而被认证方则提供相应凭证作为应答。

public interface IAuthenticationFilter
{
//对请求实施认证
void OnAuthentication(AuthenticationContext filterContext);
//将相应的认证质询发给请求者
void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
}

  

两个相关上下文对象

//
// 摘要:
// Represents an authentication context containing information for performing authentication.
public class AuthenticationContext : ControllerContext
{
public AuthenticationContext();
public AuthenticationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IPrincipal principal); //
// 摘要:
// Gets or sets the action descriptor.
//
// 返回结果:
// The action methods associated with the authentication
public ActionDescriptor ActionDescriptor { get; set; }
//
// 摘要:
// Gets or sets the currently authenticated principal.
//
// 返回结果:
// The security credentials for the authentication.
public IPrincipal Principal { get; set; }
//
// 摘要:
// Gets or sets the error result, which indicates that authentication was attempted
// and failed.
//
// 返回结果:
// The authentication result.
public ActionResult Result { get; set; }
}

  

//
// 摘要:
// Represents an authentication challenge context containing information for executing
// an authentication challenge.
public class AuthenticationChallengeContext : ControllerContext
{
public AuthenticationChallengeContext();
//
// 摘要:
// Initializes a new instance of the System.Web.Mvc.Filters.AuthenticationChallengeContext
// class.
//
// 参数:
// controllerContext:
// The controller context.
//
// actionDescriptor:
// The action methods associated with the challenge.
//
// result:
// The challenge response.
public AuthenticationChallengeContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, ActionResult result); //
// 摘要:
// Gets or sets the action descriptor.
//
// 返回结果:
// The action descriptor associated with the challenge.
public ActionDescriptor ActionDescriptor { get; set; }
//
// 摘要:
// Gets or sets the action result to execute.
//
// 返回结果:
// The challenge response.
public ActionResult Result { get; set; }
}

执行

身份认证是请求处理的第一步,所以AuthenticationFilter是最先被执行的过滤器。

所有过滤器的执行都是由ActionInvoker来驱动的,默认采用AsyncControllerActionInvoker对象。它派生自ControllerActionInvoker。

方法执行之前:

  1. 如果多个AuthenticationFilter同时应用到目标方法上,则依据相应的Order和Scope属性对它们进行排序。
  2. 根据当前ControllerContext、描述目标方法的ActionDescriptor对象及原始的Principal(对应HttpContext的User属性)创建AuthenticationContext对象。
  3. 将AuthenticationContext对象作为参数调用每个AuthenticationFilter的OnAuthentication方法实施认证。这里可以直接设置AuthenticationContext的ActionResult属性 ,则它会直接用于响应当前请求。

方法执行之后:

  1. 执行的结果总是体现为一个ActionResult对象。ControllerActionInvoker对象会根据ControllerContext,描述目标方法的ActionDescriptor对象 和这个ActionResult对象创建一个AuthenticationChallengeContext对象。
  2. 将AuthenticationChallengeContext对象作为参数逆序调用每个AuthenticationFilter的OnAuthenticationChallenge方法。这个AuthenticationChallengeContext对象的ActionResult属性返回的ActionResult对象将被用于响应当前请求。

在整个AuthenticationFilter链的执行过程中,如果某个AuthenticationFilter对象的OnAuthentication方法执行时对作为参数的AuthenticationContext对象的ActionResult属性做了相应设置,则整个“AuthenticationFilter链”的执行立即中止,指定的这个ActionResult对象将用于响应当前请求。如果在执行过程中对AuthenticationContext对象的Principal属性做了相应的设置,该属性值将会作为当前HttpContext和当前线程的Principal。

自定义AuthenticateAttribute

public class AuthenticateAttribute : FilterAttribute, IAuthenticationFilter
{
public const string AuthorizationHeaderName = "Authorization";
public const string WwwAuthenticationHeaderName = "WWW-Authenticate";
public const string BasicAuthenticationScheme = "Basic";
private static Dictionary<string, string> userAccounters; static AuthenticateAttribute()
{
userAccounters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
userAccounters.Add("Foo", "Password");
userAccounters.Add("Bar", "Password");
userAccounters.Add("Baz", "Password");
} public void OnAuthentication(AuthenticationContext filterContext)
{
IPrincipal user;
if (this.IsAuthenticated(filterContext, out user))
{
filterContext.Principal = user;
}
else
{
this.HandleUnauthenticatedRequest(filterContext);
}
} protected virtual AuthenticationHeaderValue GetAuthenticationHeaderValue(AuthenticationContext filterContext)
{
string rawValue = filterContext.RequestContext.HttpContext.Request.Headers[AuthorizationHeaderName];
if (string.IsNullOrEmpty(rawValue))
{
return null;
}
string[] split = rawValue.Split(' ');
if (split.Length != 2)
{
return null;
}
return new AuthenticationHeaderValue(split[0], split[1]);
} protected virtual bool IsAuthenticated(AuthenticationContext filterContext, out IPrincipal user)
{
user = filterContext.Principal;
if (null != user & user.Identity.IsAuthenticated)
{
return true;
} AuthenticationHeaderValue token = this.GetAuthenticationHeaderValue(filterContext);
if (null != token && token.Scheme == BasicAuthenticationScheme)
{
string credential = Encoding.Default.GetString(Convert.FromBase64String(token.Parameter));
string[] split = credential.Split(':');
if (split.Length == 2)
{
string userName = split[0];
string password;
if (userAccounters.TryGetValue(userName, out password))
{
if (password == split[1])
{
GenericIdentity identity = new GenericIdentity(userName);
user = new GenericPrincipal(identity, new string[0]);
return true;
}
}
}
}
return false;
} protected virtual void HandleUnauthenticatedRequest(AuthenticationContext filterContext)
{
string parameter = string.Format("realm=\"{0}\"", filterContext.RequestContext.HttpContext.Request.Url.DnsSafeHost);
AuthenticationHeaderValue challenge = new AuthenticationHeaderValue(BasicAuthenticationScheme, parameter);
filterContext.HttpContext.Response.Headers[WwwAuthenticationHeaderName] = challenge.ToString();
filterContext.Result = new HttpUnauthorizedResult();
} public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) { }
}

  

AuthorizationFilter

AuthorizationFilter将会在所有AuthenticationFilter执行结束后执行,因为授权检验要在认证之后才有意义。

public interface IAuthorizationFilter
{
//
// 摘要:
// Called when authorization is required.
//
// 参数:
// filterContext:
// The filter context.
void OnAuthorization(AuthorizationContext filterContext);
}

  

public class AuthorizationContext : ControllerContext
{
public AuthorizationContext();
//
// 摘要:
// Initializes a new instance of the System.Web.Mvc.AuthorizationContext class using
// the specified controller context.
//
// 参数:
// controllerContext:
// The context within which the result is executed. The context information includes
// the controller, HTTP content, request context, and route data.
[Obsolete("The recommended alternative is the constructor AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor).")]
public AuthorizationContext(ControllerContext controllerContext);
//
// 摘要:
// Initializes a new instance of the System.Web.Mvc.AuthorizationContext class using
// the specified controller context and action descriptor.
//
// 参数:
// controllerContext:
// The context in which the result is executed. The context information includes
// the controller, HTTP content, request context, and route data.
//
// actionDescriptor:
// An object that provides information about an action method, such as its name,
// controller, parameters, attributes, and filters.
public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor); //
// 摘要:
// Provides information about the action method that is marked by the System.Web.Mvc.AuthorizeAttribute
// attribute, such as its name, controller, parameters, attributes, and filters.
//
// 返回结果:
// The action descriptor for the action method that is marked by the System.Web.Mvc.AuthorizeAttribute
// attribute.
public virtual ActionDescriptor ActionDescriptor { get; set; }
//
// 摘要:
// Gets or sets the result that is returned by an action method.
//
// 返回结果:
// The result that is returned by an action method.
public ActionResult Result { get; set; }
}

AuthorizeAtrribute

标记该Action只有被认证用户才能访问 ,可以显式地对Users和Roles属性进行设置,限定范围。

授权失败则返回状态为401的响应。

对比 PrincipalPermissionAttribute特性

PrincipalPermissionAttribute通过代码访问安全检验实现对方法调用的授权。

[PrincipalPermission(SecurityAction.Demand,Name ="foo", Role = @"BUILTIN/Administrators")] 标识必须是账号为foo或拥有"BUILTIN/Administrators"角色的用户才能访问

同时它们的授权策略也不一样,PrincipalPermission 是Or的关系,如果存在多个PrincipalPermission属性,只要满足一个就可以。而Authorize是And的关系,必须同时满足。

RequestHttpsAttribute

限制必须以https请求的方式访问目标Action,

如果当前是一个Get请求,但并不是采用https的,则会自动跳转替换url为https方式的访问形式,对于非Get方式的请求则会调用HandlerNonHttpsRequest方法,可以重写这个方法。

ValidateInputAttribute

用于拦截XSS攻击等不合法的请求内容,

HttpRequestBase具有一个ValidateInput的方法用于验证请求的输入,

ControllerBase具有一个布尔属性ValidateRequest 用于标记是否需要对请求输入进行验证,这个属性的默认值是True。

ValidateInputAttribute就是通过改变Controller的ValidateRequest 属性的取值来开启或关闭验证

对比AllowHtmlAttribute属性

AllowHtmlAttribute仅针对容器成员的某个数据成员,而ValidateInputAttribute针对整个请求。

ValidateAntiForgeryTokenAttribute

ValidateAntiForgeryTokenAttribute结合HtmlHelper的AntiForgeryToken方法用于防备CSRF攻击。

当在View中调用HtmlHelper的AntiForgeryToken方法时,它会创建一个称为“防伪令牌(AntiForgeryToken)”的字符串,并返回一个类型为hidden的<input>元素对应的html,同时设置一个HttpOnly标记的Cookie,Cookie名称与当前请求的应用路径有关,Cookie值(一个AntiForgeryData对象)则经过加密,由这个Cookie值可以计算得到防伪令牌。

ChildActionOnlyAttribute

标记某action为ChildAction,即不能以Http请求的方式被直接调用,仅仅希望它在某个View中被调用以生成组成页面某个部分的Html。

ActionFiler

Context:

AsyncTimeoutAttribute, NoAsyncTimeoutAttribute

ExceptionFilter

用于处理包括目标Action方法在内的整个ActionFilter链执行过程中抛出的异常,

1. ExceptionFilter链执行顺序是反向的,即优先级越高执行顺序越靠后。

2. 将ExceptionContext的ExceptionHandled设置为true ,并不会阻止后续ExceptionFilter的执行

3. 如果ExceptionFilter在执行OnException的过程中出现异常,整个ExceptionFilter链的执行将立即中止,并且该异常被直接抛出来。

HandlerErrorAttribute

属性View和Master表示作为错误页面的View名称和对应的布局文件名,默认值分别为"Error"和空字符串。会创建一个HandlerErrorInfo对象作为Model,传递给View。

默认的异常处理注册

只有在当前HttpContext的IsCustomErrorEnabled属性是True的情况下HandleErrorAttribute才会真正被用于处理抛出的异常,

Web.config中: <system.web><customErrors mode="On|Off|RemoteOnly"/></system.web>

ResultFilter

不管目标Action方法是否有返回值,不论它返回一个怎样的对象,ControllerActionInvoker在完成了目标Action方法后总会生成一个ActionResult对象来对请求进行响应。在执行这个ActionResult的前后,应用在目标Action方法上的ReslutFilter会被执行。

ResultExecutedContext中的Exception是ActionResult执行过程中抛出的异常

OverrideFilter

Filter通过Scope属性表示被封装的过滤器的应用范围:全局注册的过滤器会自动应用到所有Controller类型上,HttpController上注册的过滤器则自动应用到定义在其中的所有Action上。

如果某个Action方法不需要这些外围注册的过滤器,则需要使用OverrideFilter这种特殊过滤器了。

[OverrideActionFilters]
[OverrideAuthentication]
[OverrideAuthorization]
[OverrideExceptionFilters]
[OverrideResultFilters]

Asp.Net MVC<五>:过滤器的更多相关文章

  1. ASP.NET MVC 过滤器(一)

    ASP.NET MVC 过滤器(一) 前言 前面的篇幅中,了解到了控制器的生成的过程以及在生成的过程中的各种注入点,按照常理来说篇幅应该到了讲解控制器内部的执行过程以及模型绑定.验证这些知识了.但是呢 ...

  2. ASP.NET MVC 过滤器(三)

    ASP.NET MVC 过滤器(三) 前言 本篇讲解行为过滤器的执行过程,过滤器实现.使用方式有AOP的意思,可以通过学习了解过滤器在框架中的执行过程从而获得一些AOP方面的知识(在顺序执行的过程中, ...

  3. ASP.NET MVC 过滤器(四)

    ASP.NET MVC 过滤器(四) 前言 前一篇对IActionFilter方法执行过滤器在框架中的执行过程做了大概的描述,本篇将会对IActionFilter类型的过滤器使用来做一些介绍. ASP ...

  4. ASP.NET MVC 过滤器(五)

    ASP.NET MVC 过滤器(五) 前言 上篇对了行为过滤器的使用做了讲解,如果在控制器行为的执行中遇到了异常怎么办呢?没关系,还好框架给我们提供了异常过滤器,在本篇中将会对异常过滤器的使用做一个大 ...

  5. ASP.NET没有魔法——ASP.NET MVC 过滤器(Filter)

    上一篇文章介绍了使用Authorize特性实现了ASP.NET MVC中针对Controller或者Action的授权功能,实际上这个特性是MVC功能的一部分,被称为过滤器(Filter),它是一种面 ...

  6. Asp.net Mvc 过滤器执行顺序

    Asp.net Mvc 过滤器执行顺序: IAuthorizationFilter(OnAuthorization)----->IActionFilter(OnActionExecuting)- ...

  7. ASP.NET MVC过滤器

    在ASP.NET MVC中有个重要特性就是过滤器,使得我们在MVC程序开发中更好的控制浏览器请求的URL,不是每个请求都有响应内容,只有特定得用户才有.园子里关于过滤器的资料也有很多,这篇文章主要是记 ...

  8. ASP.NET MVC过滤器(一)

    MVC过滤器是加在 Controller 或 Action 上的一种 Attribute,通过过滤器,MVC 网站在处理用户请求时,可以处理一些附加的操作,如:用户权限验证.系统日志.异常处理.缓存等 ...

  9. ASP.NET MVC 过滤器开发与使用

    ASP.NET MVC 中给我们提供了内置的过滤器,通过过滤器,我们可以在控制器内的方法前后,添加必须的业务逻辑,如权限验证,身份验证,错误处理等. 今天,我们主要介绍3个过滤器:OutputCach ...

  10. Asp.Net MVC过滤器小试牛刀

    在上学期间学习的Asp.Net MVC,基本只是大概马马虎虎的了解,基本处于知其然而不知其所以然.现在到上班,接触到真实的项目,才发现还不够用,于是从最简单的过滤器开始学习.不得不说MVC的过滤器真是 ...

随机推荐

  1. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q138-Q140)

    Question  138 You are designing a SharePoint 2010 application that will deploy a Web Part assembly. ...

  2. app:clean classes Exception

    Error:Execution failed for task ':app:clean'.> Unable to delete directory: C:\Users\LiuZhen\Deskt ...

  3. [转]File Descriptor泄漏导致Crash: Too many open files

    在实际的Android开发过程中,我们遇到了一些奇奇怪怪的Crash,通过sigaction再配合libcorkscrew以及一些第三方的Crash Reporter都捕获不到发生Crash的具体信息 ...

  4. iOS Xcode, 解决“Could not insert new outlet connection: Could not find any information for the class named”的问题。

    在Xcode中,我们可以在StoryBoard编辑界面或者是xib编辑界面中通过“Control键+拖拽“的方式将某个界面元素和对应的代码文件连接起来,在代码文件中创建outlet. 不过,如果你的运 ...

  5. active_record的不定时更新

    1 valid?方法只是校验所有的校验条件,如果规则A仅加在数据库上,而不是model上,是不对校验起作用的,最后提交时虽然校验成功,但仍然会抛异常.比如数据库中增加了非空的规则,但模型上没有pres ...

  6. SSIS 通过添加脚本组件 自定义转换数据

    问题:从mysql导入到sql的汉字都是乱码或者干脆导入不成功,报”截断字符串“错误,错在mysql当时建立的都是使用的默认编码latin1;搞不明白,又不是瑞典人,你用这个干毛.导致现在遇到n多问题 ...

  7. ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

    从供应商手中接手一个云平台(Windwos Azure)上的MySQL数据库,登录数据库时遇到错误: $mysql -uroot -p Enter password: ERROR 2002 (HY00 ...

  8. Unable to open the physical file xxxx. Operating system error 2

    在新UAT服务器上,需要将tempdb放置在SSD(固态硬盘)上.由于SSD(固态硬盘)特性,所以tempdb的文件只能放置在D盘下面,而不能是D盘下的某一个目录下面. ALTER  DATABASE ...

  9. kmeans算法实践

    这几天学习了无监督学习聚类算法Kmeans,这是聚类中非常简单的一个算法,它的算法思想与监督学习算法KNN(K近邻算法)的理论基础一样都是利用了节点之间的距离度量,不同之处在于KNN是利用了有标签的数 ...

  10. SpringMVC注入Spring的bean

    一.Spring和SpringMVC两个IOC容器有什么关系呢? Spring的IOC容器包含了SpringMVC的IOC,即SpringMVC配置的bean可以调用Spring配置好的bean,反之 ...