Asp.Net MVC<五>:过滤器

  1. 根据用途和执行时机的不同,ASP.NET MVC提供的过滤器可分为6种
  2. 主要对象
    1. Filter
    2. IFilterProvider
      1. FilterAttributeFilterProvider
      2. ControllerInstanceFilterProvider
      3. GlobalFilterCollection
    3. FilterProviders
    4. FilterInfo
  3. AuthenticationFilter
    1. 两个相关上下文对象
    2. 执行
    3. 自定义AuthenticateAttribute
  4. AuthorizationFilter
    1. AuthorizeAtrribute
      1. 对比 PrincipalPermissionAttribute特性
    2. RequestHttpsAttribute
    3. ValidateInputAttribute
      1. 对比AllowHtmlAttribute属性
    4. ValidateAntiForgeryTokenAttribute
    5. ChildActionOnlyAttribute
  5. ActionFiler
  6. ExceptionFilter
    1. HandlerErrorAttribute
  7. ResultFilter
  8. OverrideFilter
  9.  

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

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

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

2、 主要对象

2.1、 Filter

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
}

2.2、 IFilterProvider

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

2.2.1、 FilterAttributeFilterProvider

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

2.2.2、 ControllerInstanceFilterProvider

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

2.2.3、 GlobalFilterCollection

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

2.3、 FilterProviders

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

 

2.4、 FilterInfo

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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; }
}

  

1
2
3
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类型的过滤器时,最终有效的是依执行顺序排在最后的那一个。

 

3、 AuthenticationFilter

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

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

  

3.1、 两个相关上下文对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//
// 摘要:
//     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; }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//
// 摘要:
//     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; }
}

3.2、 执行

身份认证是请求处理的第一步,所以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。

3.3、 自定义AuthenticateAttribute 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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) { }
}

  

4、 AuthorizationFilter

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

1
2
3
4
5
6
7
8
9
10
11
public interface IAuthorizationFilter
{
    //
    // 摘要:
    //     Called when authorization is required.
    //
    // 参数:
    //   filterContext:
    //     The filter context.
    void OnAuthorization(AuthorizationContext filterContext);
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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; }
}

4.1、 AuthorizeAtrribute

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

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

4.1.1、  对比 PrincipalPermissionAttribute特性

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

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

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

4.2、 RequestHttpsAttribute

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

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

4.3、 ValidateInputAttribute

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

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

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

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

4.3.1、 对比AllowHtmlAttribute属性

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

4.4、 ValidateAntiForgeryTokenAttribute

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

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

4.5、 ChildActionOnlyAttribute

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

5、 ActionFiler

Context:

AsyncTimeoutAttribute, NoAsyncTimeoutAttribute

6、 ExceptionFilter

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

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

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

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

6.1、 HandlerErrorAttribute

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

默认的异常处理注册

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

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

7、 ResultFilter

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

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

8、 OverrideFilter

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

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

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

posted @   随心~  阅读(1306)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!

点击右上角即可分享
微信分享提示