博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mvc(7)——过滤器
阅读量:2241 次
发布时间:2019-05-09

本文共 10868 字,大约阅读时间需要 36 分钟。

过滤器(Filter)把附加逻辑注入到MVC框架的请求处理。它们提供一种简单而雅致的方式,实现了交叉关注。所谓交叉关注(Cross-CuttingConcerns),是指可以用于整个应用程序,而又不适合放置在某个局部位置的功能,否则会打破关注分离模式。典型的交叉关注例子是登录、授权以及缓存等。今天我们来学习一下MVC框架所支持的不同类型的过滤器,如何创建和使用过滤器,以及如何控制它们的执行。

1、准备项目

我们通过使用“Empty(空)”模板,并检查选项以添加核心MVC文件夹和引用,创建了一个新的名称为Filters的MVC项目。在其中创建了Home控制器,它有一个动作方法,如下所示,今天我们只关注控制器,因此从动作方法中返回了字符串值,而不是ActionResult对象一一这使MVC框架可以绕过Razor视图引擎,而直接将字符串值发送给浏览器。

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace Filters.Controllers{    public class HomeController : Controller    {        // GET: Home        public string  Index()        {            return "This is the Index action on the Home controller";        }    }}

今天我们将演示如何使用认证过滤器这一MVC新特性,为此小编需要能够执行一些简单的用户认证。小编需要在web.config文件中定义静态用户凭据,如下所示:

上面的代码中,我们定义了两个用户user和admin,并给它们分配相同的密码secret。再次使用forms

认证,并使用loginUrl属性来指定未认证的请求将被重定向到/Account/Login。
下面的代码中你可以看到添加到项目中的Account控制器的内容,它的Login动作将以默认的路由配置为目标。

using System.Web.Mvc;using System.Web.Security;namespace Filters.Controllers{    public class AccountController : Controller    {        // GET: Account        public ActionResult Login()        {            return View();        }        [HttpPost]        public ActionResult Login(string username, string password, string retrunurl)        {            bool result = FormsAuthentication.Authenticate(username,password);            if (result)            {                FormsAuthentication.SetAuthCookie(username,false);                return Redirect(retrunurl?? Url.Action("Index","Home"));//如果retrunurl不为空,则跳到retrunurl,否则跳到home控制器中的index            }            else            {                ModelState.AddModelError("","Incorrect username or password");                return View();            }        }    }}

为了创建从用户那里收集候选凭证的视图,创建Views/Shared文件夹并右击它。选择”Add(添加)”一”MVC5ViewPage(Razor)(MVC5视图页(Razor))”,设置名称为”Login.cshtml”,并单击“确定(OK)”按钮以创建视图。编辑新视图内容如下:

@{    Layout = null;}    
Login @using (Html.BeginForm()) { @Html.ValidationSummary()

}

小编希望VisualStudio从应用程序的根URL开始启动,而不是基于正在编辑的文件猜测URL。从VisualStudio的’Project(项目)”菜单选择“Filters在“StartAction(开始动作)”小节检查”SpecificPage(特定页)”选项。你不需要提供值,仅检查选项就行了。如果启动示例应用程序,出现如下结果:

这里写图片描述

2、过滤器的类型

MVC框架支持5种不同类型的过滤器。每一种类型让你能够在请求处理的不同点上引入逻辑。

过滤器类型 接口 默认实现 描述
认证过滤器 IAuthenticationFilter N/A 最先运行,在任何其他过滤器或动作方法之前,但在授权过滤器之后可以再次运行
授权过滤器 IAuthorizationFiIter AuthorizeAttribute 在认证过后,其他过滤器或动作方法之前,第二个运行
动作过滤器 IActionFilter ActionFiIterAttribute 在动作方法之前及之后运行
结果过滤器 IResu1tFilter ActionFilterAttribute 在动作结果被执行之前和之后运行
异常过滤器 IExceptionFiIter HandleErrorAttribute 仅在另一个过滤器、动作方法或动作结果抛出异常时运行

在MVC框架调用一个动作之前,会首先检测该方法的定义,以查看它是否具有实现上面表格中所列接口的注解属性。如果有,那么便在请求处理程序的相应点上调用这些接口所定义的方法。框架包含了一些默认的注解属性类,它们实现了这些过滤器接口。

3、使用授权过滤器

授权过滤器在认证过滤器之后,其他过滤器和动作方法被调用之前运行。正如其名称的含义一样,这些过滤器执行你的授权策略,以确保动作方法只被己认证用户所调用。授权过滤器实现IAuthorizationFilter接口。

namespaceSystem.Web.MVC{    public interface IAuthorizationFilter{        void OnAuthorization(AuthorizationContext filterContext);    }}

3.1、使用内建授权过滤器

直接使用AuthorizeAttribute时,可以用这个类的两个public属性来指定授权策略。

名称 类型 描述
user string 一个逗号分隔的用户名列表,允许这些用户访问该动作方法
Roles string 一个逗号分隔的角色列表。为了访问该动作方法,用户必须至少是这些角色之一
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace Filters.Controllers{    public class HomeController : Controller    {        // GET: Home        [Authorize(Users ="admin")]        public string  Index()        {            return "This is the Index action on the Home controller";        }    }}

小编己经指出授权admin用户调用Index动作方法,但这里还有一个隐含条件,即该请求己被认证。如果未指定任何用户或角色,那么任何己被认证的用户都可以使用这个动作方法。对于大多数应用程序,AuthorizeAttribute提供的授权策略己经足够。

启动程序的界面:
这里写图片描述
使用admin登录后的结果:
这里写图片描述
如果使用user用户登录,不会进入。

4、使用认证过滤器

认证过滤器是MVC第5版本的新特性,它对应用程序中的控制器和动作如何验证用户提供了细粒度的控制。

认证过滤器有一个相对复杂的生命周期。它们在其他过滤器之前运行,在其他类型的过滤器被使用之前,让你定义一个将要运用的认证策略。认证过滤器也可以结合授权过滤器对请求提出认证挑战,而该认证挑战又不遵循授权策略。认证过滤器也可以在一个动作方法执行之后,但在ActionResult被处理之前运行。

4.1理解IAuthenticationFilter接口

认证过滤器实现了IAuthenticationFilter接口

namespace System.Web.MVC.Filters{    public interface IAuthenticationFilter    {        void OnAuthentication(Authenticationcontext context);        void OnAuthenticationCha11enge(AuthenticationChallengeContext context);    }}

无论对认证的请求或对动作方法授权策略的请求失败,MVC框架都调用OnAuthenticationChallenge方法,并给该方法传递一个AuthenticationChallengeContext对象,下表是AuthenticationChallengeContext

类的一些附加属性。

名称 描述
ActionDescriptor 返回描述动作方的ActionDescriptor,这些动作方法上运用了过滤器
Result 设置表示认证质疑结果的ActionResult

  上表中,最重要的属性是Result,因为它允许认证过滤器传递一个ActionResult给MVC框架,小编将简要描述短路这一过程。

  但是解释认证过滤器工作机制的最好方式是通过示例。在小编看来,认证过滤器最有趣的方面是,它们允许一个单一的控制器定义动作方法,而这些动作方法将以不同的方式进行验证,因此第一步是添加一个新的模拟Google登录的控制器。

//google登录控制器代码如下:using System.Web.Mvc;using System.Web.Security;namespace Filters.Controllers{    public class GoogleAccountController : Controller    {        // GET: GoogleAccount        public ActionResult Login()        {            return View();        }        [HttpPost]        public ActionResult Login(string username,string password,string returnUrl)        {            if (username.EndsWith("@google.com") && password == "secret")            {                FormsAuthentication.SetAuthCookie(username,false);                return Redirect(returnUrl ?? Url.Action("Index","Home"));            }            else            {                ModelState.AddModelError("","Incorrect username or password");                return View();            }        }    }}

  因为这仅仅是一个示例,为此小编建立了一个可怕的攻击,只要提供密码secret,它将认证所有以google.com结尾的用户名。

  此刻,小编的认证控制器没有连接应用程序,而是引入了认证过滤器。在Infrastructure文件夹中,小编创建了一个新的类文件,名称为GoogleAuthAttribute.cs,代码如下,在如下代码中FilterAttribute类(GoogleAuth过滤器派生自它)是所有类的基础。

using System.Web.Mvc.Filters;using System.Web.Mvc;using System.Web.Routing;namespace Filters.Infrastructure{    public class GoogleAuthAttribute:FilterAttribute,IAuthenticationFilter    {        public void OnAuthentication(AuthenticationContext context)        {            //未实现        }        public void OnAuthenticationChallenge(AuthenticationChallengeContext context)        {            if (context.Result==null)            {                context.Result = new RedirectToRouteResult(                    new RouteValueDictionary {                        { "controller","GoogleAccount" },                        { "action","Login"},                        { "returnUrl",context.HttpContext.Request.RawUrl}                    }                    );            }        }    }}

OnAuthenticationChallenge方法的实现检查AuthenticationChallengeContext参数的Result属性是否己经设置。当动作方法执行后运行过滤器时,这让我们避免质疑用户。不用担心,我们稍后在处理中将解释为什么这点很重要。

在这一部分最重要的是,通过重定向用户浏览器到带有RedirectToRouteResuIt的GoogleAccount控制器,使用OnAuthenticationChallenge方法来检测用户的凭据。

4.2 实现认证检查

  认证过滤器己经准备好对用户伪造的Google凭据提出挑战,现在可以接上剩余的行为。在运行其他类型的过滤器之前,控制器将调用OnAuthentication方法,以提供机会执行更大范围的认证检查。你不需要实现OnAuthentication方法,但小编打算这样做是为了检查自己正在处理一个Google账号。

  像AuthenticationChallengeContext类一样,OnAuthentication方法被传递了一个AuthenticationContext对象,该对象派生于Controllerconte。
Authenticationcontext类定义的属性如下表所示:

名称 描述
ActionDescriptor 返回一个描述动作方法的Actionoescriptor,这些动作方法已经运用了过滤器
Principal 如果用户己经被认证,返回一个识别当前用户的Iprincipal实现
Result 设置一个表示认证检查结果的ActionResult

  如果OnAuthentication为上下文对象的Result属性设置了一个值,那么MVC框架将调用OnAuthenticationChallenge方法。如果未设置,那么OnAuthentication中的一个方法将被执行。使用OnAuthentication方法创建一个报告用户凭据错误的结果,它可以被OnAuthenticationChallenge方法重载以便对用户凭据提出质疑。这让小编确信,他们看到了一个有意义的响应,即使没有发出挑战(但是小编必须承认己经碰到了这种情况)。在下面的代码中,你可以看到如何实现OnAuthentication方法,以便检查通过使用Google证书请求已经被认证。

using System.Web.Mvc.Filters;using System.Web.Mvc;using System.Web.Routing;using System.Security.Principal;//新增代码namespace Filters.Infrastructure{    public class GoogleAuthAttribute:FilterAttribute,IAuthenticationFilter    {        public void OnAuthentication(AuthenticationContext context)        {   //新增            IIdentity ident = context.Principal.Identity;            if (!ident.IsAuthenticated || !ident.Name.EndsWith("@google.com"))            {                context.Result = new HttpUnauthorizedResult();            }        }        public void OnAuthenticationChallenge(AuthenticationChallengeContext context)        {            if (context.Result==null || context.Result is HttpUnauthorizedResult)//修改代码            {                context.Result = new RedirectToRouteResult(                    new RouteValueDictionary {                        { "controller","GoogleAccount" },                        { "action","Login"},                        { "returnUrl",context.HttpContext.Request.RawUrl}                    }                    );            }        }    }}

  OnAuthentication方法的实现检查使用以@google.com结尾的用户名的请求是否己经被认证。如请求未被认证,或使用不同种类的凭据认证,那么小编将把AuthenticationContext对象的Result属性设置成一个新的HttpUnauthorizedResult。

  HttpUnauthorizedResult被设置为传递给OnAuthenticationChallenge方法的
AuthenticationChallengeContext对象的Result值,你可以看到,小编己经更新了此方法以对用户提出申请,当这一切发生的时候,协调过滤器中两个方法的动作。下一步是将过滤器运用于home控制器,代码如下:

using System.Web.Mvc;using Filters.Infrastructure;//需要引用namespace Filters.Controllers{    public class HomeController : Controller    {        // GET: Home        [Authorize(Users ="admin")]        public string  Index()        {            return "This is the Index action on the Home controller";        }        [GoogleAuth] //新增        public string List() //新增        {            return "This is the list action on the Home controller";        }    }}

  小编己经定义了一个新的动作方法,名称为“List”,该方法用GoogleAuth过滤器修饰。结果是,通过内置支持表单认证对Index方法的访问是安全的,而且通过自定义伪造Google认证系统对List动作的访问也是安全的。

  启动应用程序可以看到效果。默认情况下,浏览器将以Index动作方法为目标,这将触发标准认证,并要求你使用在web.config文件中定义的用户名登录。
  如果你请求/Home/List,那么你现有的凭据将被拒绝,你将不得不使用Google的用户名认证。
启动程序:
这里写图片描述

输入账号密码

这里写图片描述
运行结果:
这里写图片描述
如果直接输入/Home/List
当输入网址后,回车后会跳到account/login 的等界面。
如果要是输入谷歌账号:
这里写图片描述
结果如下:
这里写图片描述

4.3、组合认证和授权过滤器

其实,我们可以在同一个动作方法组合认证和授权过滤器,以缩小安全策略的范围。MVC框架将调用认证过滤器的OnAuthentication方法,正如前面的示例,如果请求通过了认证检查,那么转去运行授权过滤器。如果请求没有传递给授权过滤器,那么将调用认证过滤器的OnAuthenticationChallenge方法,这样就可以对请求凭据的用户提出挑战。在接下来的代码中,你可以看到小编组合了GoogleAuth和Authorize过滤器以限制对Home控制器中List动作的访问。

using System.Web.Mvc;using Filters.Infrastructure;namespace Filters.Controllers{    public class HomeController : Controller    {        // GET: Home        [Authorize(Users ="admin")]        public string  Index()        {            return "This is the Index action on the Home controller";        }        [GoogleAuth]        [Authorize(Users ="bob@google.com")]//新增        public string List()        {            return "This is the list action on the Home controller";        }    }}

  Authorize过滤器限制访问bob@google.com账号。如果另一个Google账号以该动作方法为目标,那么认证过滤器OnAuthenticationCha11enge方法将被传递一个AuthenticationChallengecontext对象,该对象的Result属性被设置成HttpUnauthorizedResu1t类的实例(这是小编为什么在OnAuthentication方法中使用相同类的原因)。

  对使用AccountController认证的用户admin,Home控制器中的过滤器限制访问Index方法,而对通过GoogleAccount控制器认证的bogoogle跹om用户,限制访问List方法。
启动程序:
这里写图片描述
输入账号admin 密码:secret后的结果:
这里写图片描述
导航到/home/list,会自动跳转到认证页面: 输入账号:google@google.com 密码secret后的结果:
这里写图片描述
结果:无法访问。


在此网址,输入账号:bob@google.com 密码:secret 结果:

这里写图片描述
这里写图片描述

程序源码:

你可能感兴趣的文章
idea如何显示git远程与本地的更改对比?
查看>>
Git 分支 - 分支的新建与合并
查看>>
git创建与合并分支
查看>>
23种设计模式介绍以及在Java中的实现
查看>>
如何把本地项目上传到Github
查看>>
Git的使用--如何将本地项目上传到Github
查看>>
zookeeper客户端命令行查看dubbo服务的生产者和消费者
查看>>
intellij idea 相关搜索快捷键
查看>>
oracle查看数据库连接池中最大连接数和当前用户连接数等信息
查看>>
oracle中创建同义词(synonyms)表
查看>>
建立DB-LINK和建立视图
查看>>
普通视图和物化视图的区别(转)
查看>>
物化视图加DBLINK实现数据的同步_20170216
查看>>
Redis在京东到家的订单中的使用
查看>>
idea 注释模板设置
查看>>
单例模式singleton为什么要加volatile
查看>>
Oracle_spatial的空间操作符
查看>>
SDO_GEOMETRY结构说明
查看>>
oracle 的 SDO_GEOMETRY
查看>>
往oracle中插入geometry的两种方式
查看>>