Asp.net mvc 知多少(八)

本系列主要翻译自《ASP.NET MVC Interview Questions and Answers 》- By Shailendra Chauhan,想看英文原版的可访问http://www.dotnettricks.com/free-ebooks自行下载。该书主要分为两部分,ASP.NET MVC 5、ASP.NET WEB API2。本书最大的特点是以面试问答的形式进行展开。通读此书,会帮助你对ASP.NET MVC有更深层次的理解。
由于个人技术水平和英文水平也是有限的,因此错误在所难免,希望大家多多留言指正。
系列导航
Asp.net mvc 知多少(一)
Asp.net mvc 知多少(二)
Asp.net mvc 知多少(三)
Asp.net mvc 知多少(四)
Asp.net mvc 知多少(五)
Asp.net mvc 知多少(六)
Asp.net mvc 知多少(七)
Asp.net mvc 知多少(八)
Asp.net mvc 知多少(九)
Asp.net mvc 知多少(十)

本节主要讲解过滤器

Q78. 介绍下ASP.NET MVC中的 Filters(过滤器) 和 Attributes(特性)?
Ans. ASP.NET MVC 提供了一种简单的方式在action执行之前或之后注入一段代码或逻辑,它就是ASP.NET MVC attributes,通过在Controller或者Action上使用Attributes来修饰即可。可以自定义过滤器或特性通过实现ASP.NET
MVC filter 接口或继承并重载ASP.NET MVC filter attribute类。
通常,过滤器被用来执行以下常见的功能点:

  1. Custom Authentication(自定义认证)
  2. Custom Authorization (User based or Role based)(自定义授权-基于用户或角色)
  3. Error handling or logging(异常处理或记录日志)
  4. User Activity Logging(用户活动日志)
  5. Data Caching(数据缓存)
  6. Data Compression(数据压缩)

Q79. 介绍下ASP.NET MVC中几种不同的Filters(过滤器) ?
Ans. 主要有以下五种类型Filters:
Authentication Filters(认证过滤器) - 该过滤器是从ASP.NET MVC5中引入的。IAuthenticationFilter接口是用来创建自定义认证过滤器。IAuthenticationFilter定义如下:

public interface IAuthenticationFilter
{
 void OnAuthentication(AuthenticationContext filterContext);
 void OnAuthenticationChallenge(AuthenticationChallengeContext
filterContext);
}

通过实现IAuthenticationFilter接口,即可实现自定义的认证过滤特性。

public class CustomAuthenticationFilterAttribute : FilterAttribute,
IAuthenticationFilter
{
 public void OnAuthentication(AuthenticationContext filterContext)
 {
 filterContext.HttpContext.Response.Write("Authentication
Filter<br/>");
 }
 //Runs after the OnAuthentication method
 public void OnAuthenticationChallenge(AuthenticationChallengeContext
filterContext)
 {
 //TODO: Additional tasks on the request
 }
}

Authorization Filters(授权过滤器) - ASP.NET MVC的授权过滤器实现了IAuthorizationFilter接口。

public interface IAuthorizationFilter
{
 void OnAuthorization(AuthorizationContext filterContext);
}

AuthorizeAttribute提供了以下可重载的方法:

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
 protected virtual bool AuthorizeCore(HttpContextBase httpContext);
 protected virtual void HandleUnauthorizedRequest(AuthorizationContext
filterContext);
public virtual void OnAuthorization(AuthorizationContext filterContext);
 protected virtual HttpValidationStatus
OnCacheAuthorization(HttpContextBase httpContext);
}

所以,我们可以通过实现IAuthorizationFilter接口或者继承AuthorizeAttribute类然后重载虚方法来创建自定义的授权过滤器。

Action Filters(操作过滤器) - Action filters 在action执行之前和之后执行。IActionFilter接口提供了OnActionExecutingOnActionExecuted 方法分别对应action之前和action之后执行。

public interface IActionFilter
{
 void OnActionExecuting(ActionExecutingContext filterContext);
 void OnActionExecuted(ActionExecutedContext filterContext);
}

** Result Filters(结果过滤器)** - Result filters在为action生成结果之前和和之后执行。返回的结果可以是 ViewResult、PartialViewResult、RedirectToRouteResult、RedirectResult、ContentResult、JsonResult、FileResult 和 EmptyResult ,它们均继承自 ActionResult 类。Result filters 在Action Filters之后调用。
IResultFilter接口提供了OnResultExecutingOnResultExecuted 方法分别对应生成结果之前和之后执行。

public interface IResultFilter
{
 void OnResultExecuted(ResultExecutedContext filterContext);
 void OnResultExecuting(ResultExecutingContext filterContext);
}

** Exception Filters (异常过滤器)** - Exception filters在action或者过滤器执行期间出现异常时执行。IExceptionFilter接口提供了OnException方法来处理异常。

public interface IExceptionFilter
{
 void OnException(ExceptionContext filterContext);
}

HandleErrorAttribute类实现了IExceptionFilter接口。
HandleError接收到异常,它会直接返回ASP.NET MVC Views/Shared 文件夹下的Error视图。


Q80. ASP.NET MVC的 Exception filters(异常过滤)何时执行?
Ans. Exception filters 在ASP.NET MVC pipeline(管道)执行期间有一个未处理的异常抛出时被执行。


Q81. ASP.NET MVC中filters(过滤器)的执行顺序是?
Ans. 所有的 ASP.NET MVC filter都是按照一定的顺序执行。 执行顺序为:

  1. Authentication filters(认证过滤器)
  2. Authorization filters(授权过滤器)
  3. Action filters(操作过滤器)
  4. Result filters(结果过滤器)

Q82. ASP.NET MVC中如何配置过滤器?
Ans. 我们可以配置自定义的过滤器为以下三个级别:
Global level(全局级别)
将过滤器注册到Global.asax.cs文件的Application_Start方法中:

protected void Application_Start()
{ FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}

Controller level(控制器级别)
通过将过滤器标记在controller上即可。

[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
 //TODO:
}

Action level (操作级别)
通过将过滤器标记在action上即可。

public class UserController : Controller
{
 [Authorize(Users = "User1,User2")]
 public ActionResult LinkLogin(string provider)
 {
 // TODO:
 return View();
 }
}

Q83. ASP.NET MVC中认证和授权是如何工作的?
Ans. 像 ASP.NET一样,MVC 也支持 Windows 和Forms 认证。可以通过在Web.config中配置或自己编码。


Q84. ASP.NET MVC中 Forms Authentication 和 Authorization(表单认证和授权)是如何工作的?
Ans. 和 ASP.NET一样, MVC Forms authentication在IIS认证完成之后发生。可以在 ASP.NET MVC应用程序中的Web.config文件的forms节点进行配置。
默认的表单认证配置如下:

<system.web>
<authentication mode="Forms">
<forms loginUrl="Login.aspx"
protection="All"
timeout="30"
name=".ASPXAUTH"
path="/"
requireSSL="false"
slidingExpiration="true"
defaultUrl="default.aspx"
cookieless="UseDeviceProfile"
enableCrossAppRedirects="false" />
</authentication>
</system.web>
表单认证控制流程

SetAuthCookie()RedirectFromLoginPage()被调用时FormsAuthentication类自动创建认证Cookie。 Authentication cookie(认证Cookie)中包含一个已经加密和签名的FormsAuthenticationTicket对象的字符串。
可以指定cookie的名称、 版本、目录路径、生效日期、过期日期、是否永久属性来创建FormsAuthenticationTicket对象 。

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "userName",
DateTime.Now,
DateTime.Now.AddMinutes(30), // value of time out property
false, // Value of IsPersistent property
String.Empty, FormsAuthentication.FormsCookiePath);

然后就可以使用FormsAuthentication类的Encrypt方法来加密ticket。
string encryptedTicket = FormsAuthentication.Encrypt(ticket);


Q85. ASP.NET MVC中如何实现自定义Forms Authentication and Authorization(表单认证和授权)?
Ans. 当标准的认证不能满足你的需求,你就需要去修改认证机制去创建自定义的认证方案。一个用户上下文有一个Principal,这个Principal代表用户的身份(Identity)和角色(Role)。用户通过ta的身份进行认证,通过给用户分配角色来进行授权。

用户上下文

ASP.NET 提供了IPrincipalIIdentity接口来表示用户的身份和角色。这两个接口
绑定到HttpContext对象和当前线程。可以通过实习这两个接口来创建自定义的方案。

public class CustomPrincipal : IPrincipal
{
 public IIdentity Identity { get; private set; }
 public bool IsInRole(string role)
 {
 if (roles.Any(r => role.Contains(r)))
 {
 return true;
 }
 else
{
 return false;
 }
 }
 public CustomPrincipal(string Username)
 {
 this.Identity = new GenericIdentity(Username);
 }
 public int UserId { get; set; }
 public string FirstName { get; set; }
 public string LastName { get; set; }
 public string[] roles { get; set; }
}

现在你就可以把CustomPrincipal对象放入thread(线程)的 CurrentPrincipal属性和HttpContext的User属性来完成自定义的认证和授权流程。
如果IsAuthenticated返回true则表示用户认证成功。我们可以用以下两种方式来完成对用户的验证。

  1. Thread.CurrentPrincipal.Identity.IsAuthenticated
  2. HttpContext.Current.User.Identity.IsAuthenticated

ASP.NET MVC 提供了Authorization授权过滤器来对用户授权。该过滤器可适用于action级别、控制器级别和全局级别。该过滤器基于AuthorizeAttribute特性类,可以通过继承该特性并重载OnAuthorization()方法来对授权过滤器进行自定义。

public class CustomAuthorizeAttribute: AuthorizeAttribute {
    protected virtual CustomPrincipal CurrentUser {
        get {
            return HttpContext.Current.User as CustomPrincipal;
        }
    }
    public override void OnAuthorization(AuthorizationContext filterContext) {
        if (filterContext.HttpContext.Request.IsAuthenticated) {

            if (!String.IsNullOrEmpty(Roles)) {
                if (!CurrentUser.IsInRole(Roles)) {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new {
                        controller = "Error",
                        action = "AccessDenied"
                    }));
                }
            }
            if (!String.IsNullOrEmpty(Users)) {
                if (!Users.Contains(CurrentUser.UserId.ToString())) {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new {
                        controller = "Error",
                        action = "AccessDenied"
                    }));
                    // base.OnAuthorization(filterContext); //returns to login url
                }
            }
        }
    }
}

现在你就可以像下面这样应用自定义的授权过滤器在控制器级别或者action级别。

[CustomAuthorize(Roles= "Admin")]
public class AdminController : BaseController
{
 public ActionResult Index()
 {
 return View();
 }
}

Q86. ASP.NET MVC如何允许输入html tags?
Ans. ASP.NET MVC默认不允许用户去提交html去避免Cross Site Scripting(CSS)攻击 。
ValidateInput特性可以在action级别或controller级别启用或禁用输入校验。

[ValidateInput(false)]
public class HomeController : Controller
{
 public ActionResult AddArticle()
 {
 return View();
 }
}

ValidateInput特性对所有属性都允许html tag输入,但这是不安全的。 如果你只是想针对部分属性允许html输入,可以通过为属性添加AllowHtml 特性。

public class BlogModel
{
 [Required]
 [Display(Name = "Title")]
 public string Title { get; set; }
 [AllowHtml]
 [Required]
 [Display(Name = "Description")]
 public string Description { get; set; }
}

推荐阅读更多精彩内容