在webform中,验证的流程大致如下图:

在AOP中:

在Filter中:

AuthorizeAttribute权限验证

登录后有权限控制,有的页面是需要用户登录才能访问的,需要在访问页面增加一个验证,也不能每个action都一遍。

1、写一个CustomAuthorAttribute,继承自AuthorizeAttribute,重写OnAuthorization方法,在里面把逻辑写成自己的。

2、有方法注册和控制器注册。

3、有全局注册,全部控制器全部action都生效。

但是在这个里面,首先要验证登录首页,首页没有邓丽,就跑到登录页面了,但是登录页面也要走特性里面的逻辑,又重定向到邓丽。。。循环了。。。。

这里有一个AlloAnonymous,这个标签就可以解决这个循环的问题,匿名支持,不需要登录就可以,但是单单加特性是没有用的,其实需要验证时支持,甚至可以说自己自定义一个特性也是可以的,这个特性里面是空的,只是为了用来做标记。

特性的使用范围,希望特性通用,在不同的系统,不同的地址登录,==》在特性上面加个传参的构造函数。

 public class CustomAllowAnonymousAttribute : Attribute
{
}

CustomAuthorAttribute类

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private Logger logger = new Logger(typeof(CustomAuthorizeAttribute));
private string _LoginUrl = null;
public CustomAuthorizeAttribute(string loginUrl = "~/Home/Login")
{
this._LoginUrl = loginUrl;
}
//public CustomAuthorizeAttribute(ICompanyUserService service)
//{
//}
//不行 public override void OnAuthorization(AuthorizationContext filterContext)
{
var httpContext = filterContext.HttpContext;//能拿到httpcontext 就可以为所欲为 if (filterContext.ActionDescriptor.IsDefined(typeof(CustomAllowAnonymousAttribute), true))
{
return;
}
else if (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(CustomAllowAnonymousAttribute), true))
{
return;
}
else if (httpContext.Session["CurrentUser"] == null
|| !(httpContext.Session["CurrentUser"] is CurrentUser))//为空了,
{
//这里有用户,有地址 其实可以检查权限
if (httpContext.Request.IsAjaxRequest())
//httpContext.Request.Headers["xxx"].Equals("XMLHttpRequst")
{
filterContext.Result = new NewtonJsonResult(
new AjaxResult()
{
Result = DoResult.OverTime,
DebugMessage = "登陆过期",
RetValue = ""
});
}
else
{
httpContext.Session["CurrentUrl"] = httpContext.Request.Url.AbsoluteUri;
filterContext.Result = new RedirectResult(this._LoginUrl);
//短路器:指定了Result,那么请求就截止了,不会执行action
}
}
else
{
CurrentUser user = (CurrentUser)httpContext.Session["CurrentUser"];
//this.logger.Info($"{user.Name}登陆了系统");
return;//继续
}
//base.OnAuthorization(filterContext);
}
}

Filter生效机制

为什么加个标签,继承AuthorizeAttribute,重写OnAuthorization方法就可以了呢?控制器已经实例化,调用ExecuteCore方法,找到方法名字,ControllerActionInvokee.InvokeAction,找到全部的Filter特性,InvokeAuthorize--result不为空,直接InvokeActionResult,为空就正常执行Action。

有一个实例类型,有一个方法名称,希望你反射执行

在找到方法后,执行方法前,可以检测下特性,来自全局的、来自控制器的、来自方法的。价差特性,特性是自己预定义的,按类执行,定个标识,为空就正常,不为空就跳转,正常就继续执行。

Filter原理和AOP面向切面编程

Filter是AOP思想的一种实现,其实就是ControllerActionInvoke这个类中,有个InvokeAction方法,控制器实例化之后,ActionInvoke前后,通过检测预定义Filter并且执行它,达到AOP的目的。

下面是InvokeAction的源码:

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null)
{
FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
try
{
AuthenticationContext authenticationContext = this.InvokeAuthenticationFilters(controllerContext, filters.AuthenticationFilters, actionDescriptor);
if (authenticationContext.Result != null)
{
AuthenticationChallengeContext authenticationChallengeContext = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authenticationContext.Result);
this.InvokeActionResult(controllerContext, authenticationChallengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
AuthenticationChallengeContext authenticationChallengeContext2 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authorizationContext.Result);
this.InvokeActionResult(controllerContext, authenticationChallengeContext2.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ControllerActionInvoker.ValidateRequest(controllerContext);
}
IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
AuthenticationChallengeContext authenticationChallengeContext3 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, actionExecutedContext.Result);
this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, authenticationChallengeContext3.Result ?? actionExecutedContext.Result);
}
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception exception)
{
ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
this.InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
return false;
}

全局异常处理HandleErrorAttribute

关于异常处理的建议:

  1、避免UI层直接看到异常,每个控制器里面try-catch一下?不是很麻烦吗?

  2、这个时候,AOP就登场了,HandleErrorAttribute,自己写一个特性,继承之HandleErrorAttribute,重写OnException,在发生异常之后,会跳转到这个方法。

在这边,一定要

 public class CustomHandleErrorAttribute : HandleErrorAttribute
{
private Logger logger = new Logger(typeof(CustomHandleErrorAttribute)); /// <summary>
/// 会在异常发生后,跳转到这个方法
/// </summary>
/// <param name="filterContext"></param>
public override void OnException(ExceptionContext filterContext)
{
var httpContext = filterContext.HttpContext;//"为所欲为"
if (!filterContext.ExceptionHandled)//没有被别的HandleErrorAttribute处理
{
this.logger.Error($"在响应 {httpContext.Request.Url.AbsoluteUri} 时出现异常,信息:{filterContext.Exception.Message}");//
if (httpContext.Request.IsAjaxRequest())
{
filterContext.Result = new NewtonJsonResult(
new AjaxResult()
{
Result = DoResult.Failed,
DebugMessage = filterContext.Exception.Message,
RetValue = "",
PromptMsg = "发生错误,请联系管理员"
});
}
else
{
filterContext.Result = new ViewResult()//短路器
{
ViewName = "~/Views/Shared/Error.cshtml",
ViewData = new ViewDataDictionary<string>(filterContext.Exception.Message)
};
}
filterContext.ExceptionHandled = true;//已经被我处理了
}
}
}

这个是要重新跳转的地址:

一定要考虑到是不是Ajax请求的

多种异常情况,能不能进入自定义的异常呢?

1、Action异常,没有被Catch

2、Action异常,被Catch

3、Action调用Service异常

4、Action正常视图出现异常了

5、控制器构造出现异常

6、Action名称错误

7、任意地址错误

8、权限Filter异常

答案:

1、可以

2、不可以

3、可以,异常冒泡

4、可以,为什么呢?因为ExecuteResult是包裹在try里面的

5、不可以的,Filter是在构造完成控制之后方法执行之前完成的

6、不可以的,因为请求都没进MVC流程

7、不可以的,因为请求都没进MVC

8、可以的,权限Filter也是在try里面的。

那这些没有被捕获的异常怎么办?还有一个方法

在Global中增加一个事件

 public class MvcApplication : System.Web.HttpApplication
{
private Logger logger = new Logger(typeof(MvcApplication));
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();//注册区域
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);//注册全局的Filter
RouteConfig.RegisterRoutes(RouteTable.Routes);//注册路由
BundleConfig.RegisterBundles(BundleTable.Bundles);//合并压缩 ,打包工具 Combres
ControllerBuilder.Current.SetControllerFactory(new ElevenControllerFactory()); this.logger.Info("网站启动了。。。");
}
/// <summary>
/// 全局式的异常处理,可以抓住漏网之鱼
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Application_Error(object sender, EventArgs e)
{
Exception excetion = Server.GetLastError();
this.logger.Error($"{base.Context.Request.Url.AbsoluteUri}出现异常");
Response.Write("System is Error....");
Server.ClearError(); //Response.Redirect
//base.Context.RewritePath("/Home/Error?msg=")
}

HandleErrorAttribute+Application_Error,粒度不一样,能拿到的东西不一样

IActionFilter扩展定制

IActionFilter

1、OnActionExecuting   方法执行前

2、OnActionExecuted方法执行后

3、OnResultExecuting结果执行前

4、OnResultExecuted结果执行后

先执行权限Filter,再执行ActionFilter。

执行的顺序:

  Global OnActionExecuting

  Controller OnActionExecuting

  Action OnActionExecuting

  Action真实执行

  Action OnActionExecuted

  Controller OnActionExecuted

  Global OnActionExecuted

不同位置注册的生效顺序:全局---》控制器-----》Action

好像一个俄罗斯套娃,或者说洋葱模型

在同一个位置注册的生效顺序,同一个位置按照先后顺序生效,还有一个Order的参数,不设置Order默认是1,设置之后按照从小到大执行

ActionFilter能干什么?

日志、参数检测、缓存、重写视图、压缩、防盗链、统计访问、不同的客户端跳转不同的页面、限流.....

浏览器请求时,会声明支持的格式,默认的IIS是没有压缩的,检测了支持的格式,在响应时将数据压缩(IIS服务器完成的),在响应头里面加上Content-Encoding,浏览器查看数据格式,按照浏览器格式解压(无论你是什么东西,都可以压缩解压的),压缩是IIS,解压是浏览器的。

 public class CompressActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//foreach (var item in filterContext.ActionParameters)
//{
// //参数检测 敏感词过滤
//}
var request = filterContext.HttpContext.Request;
var respose = filterContext.HttpContext.Response;
string acceptEncoding = request.Headers["Accept-Encoding"];//检测支持格式
if (!string.IsNullOrWhiteSpace(acceptEncoding) && acceptEncoding.ToUpper().Contains("GZIP"))
{
respose.AddHeader("Content-Encoding", "gzip");//响应头指定类型
respose.Filter = new GZipStream(respose.Filter, CompressionMode.Compress);//压缩类型指定
}
}
} public class LimitActionFilterAttribute : ActionFilterAttribute
{
private int _Max = ;
public LimitActionFilterAttribute(int max = )
{
this._Max = max;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string key = $"{filterContext.RouteData.Values["Controller"]}_{filterContext.RouteData.Values["Action"]}";
//CacheManager.Add(key,) 存到缓存key 集合 时间
filterContext.Result = new JsonResult()
{
Data = new { Msg = "超出频率" }
};
}
}

Filter这么厉害,有没有什么局限性????

虽然很丰富,但是只能以Action为单位,Action内部调用别的类库,加操作就做不到!这种就得靠IOC+AOP扩展。

本篇只是介绍了.NET Framework MVC 中的过滤器Filter(权限特性、Action、Result、Exception),其实在.NET Core MVC 增加了ResourceFilter,加了这个特性,资源特性,Action/Result /Exception三个特性没有什么变化。后面记录到到.NET Core MVC时再详细介绍。

.NET MVC5简介(四)Filter和AuthorizeAttribute权限验证的更多相关文章

  1. 关于filter web api mvc 权限验证 这里说的够详细了。。。

    参考:http://www.cnblogs.com/willick/p/3331520.html Filter(筛选器)是基于AOP(面向方面编程)的设计,它的作用是对MVC框架处理客户端请求注入额外 ...

  2. Asp.net下使用HttpModule模拟Filter,实现权限控制

    在asp.net中,我们为了防止用户直接从Url中访问指定的页面而绕过登录验证,需要给每个页面加上验证,或者是在模板页中加上验证.如果说项目比较大的话,添加验证是一件令人抓狂的事情,本次,我就跟大家分 ...

  3. ASP.MVC 基于AuthorizeAttribute权限设计案例

    ASP.MVC上实现权限控制的方法很多,比如使用AuthorizeAttribute这个特性 1.创建自定义特性用于权限验证 public class AuthorizeDiy : Authorize ...

  4. Struts2(十四)拦截器实现权限管理

    一.认识拦截器 拦截器也是一个类 拦截器可以在Action被调用之前和之后执行代码 框架很多核心功能是拦截器实现的 拦截器的特点: 拦截器自由组合,增强了灵活性.扩展性.有利于系统解耦 拦截器可以拦截 ...

  5. 基于.net mvc的校友录(五、web.config对的配置以及filter实现的权限控制)

    web.config配置文件 此文件是整个系统的配置中心,它告诉iis服务器本网站需要哪些运行时环境,需要哪些环境,将要进行哪些操作,开发人员也会将一个常量性的数据放在此配置中,以备系统全局调用.此文 ...

  6. MVC 自定义AuthorizeAttribute 实现权限验证

    MVC内置的AuthorizeFilter先于Action/Result过滤器执行,为网站权限验证提供了很好的一套验证机制. 通过自定义的AuthorizeAttribute可以实现对用户权限的验证. ...

  7. logback -- 配置详解 -- 四 -- <filter>

    附: logback.xml实例 logback -- 配置详解 -- 一 -- <configuration>及子节点 logback -- 配置详解 -- 二 -- <appen ...

  8. 权限验证AuthorizeAttribute

    /// <summary> /// 权限验证属性. /// </summary> public class AuthorizeExAttribute : AuthorizeAt ...

  9. Django - Xadmin (四) Filter

    Django - Xadmin (四) Filter Filter 功能描述 与 admin 组件中 Filter 功能类似,在展示页面右侧放置一列标签,通过点击这些标签来筛选出该标签相关的数据. 比 ...

随机推荐

  1. angularjs中directive指令与component组件有什么区别?

     壹 ❀ 引 我在前面花了两篇博客分别系统化介绍了angularjs中的directive指令与component组件,当然directive也能实现组件这点毋庸置疑.在了解完两者后,即便我们知道co ...

  2. 几种常见的css布局_l流体布局、圣杯布局、双飞翼布局

    1.流体布局: <!DOCTYPE html><html> <head> <meta charset="utf-8"> <ti ...

  3. IT兄弟连 HTML5教程 CSS3揭秘 CSS3属性5

    9  透明属性 元素透明也是我们常用的样式,在CSS2中使用滤镜属性opacity实现透明效果.现在有了CSS3的rgba属性,就不用这么麻烦了,当然这也得要浏览器支持才行.通常我们定义颜色都是用十六 ...

  4. Latex学习与使用

    Table of Contents Latex学习与使用 简介 文档结构 排版 表格 图片 公式 索引 简介 Latex(发音lay-tek)是一个用来产生专业文档的系统,但它并不是一个单词处理器.它 ...

  5. intelj idea中除了Find Usage外的另一种查找级联调用的方法

    一.方法 平时都是使用find usage来找一个方法的引用的地方,但是只能查看一级,今天偶然发现个查看多级的办法: 在源码编辑界面:

  6. PHP 核心特性 - 错误处理

    错误与异常 错误,可以理解程序本身的错误,例如语法错误.而异常则更偏向于程序运行不符合预期或者不符合正常流程:对于 PHP 语言而言,处理错误和处理异常使用的机制完全不同,因此很容易让人产生困惑. 例 ...

  7. ASP.NET MVC教程六:两个配置文件详解

    前言 在新建完一个MVC项目之后,你会发现整个整个项目结构中存在有两个web.config文件,如下图所示: 这两个配置文件,一个位于项目的根目录下面,一个位于Views文件夹下面,这两个配置文件有什 ...

  8. Flask 教程 第五章:用户登录

    本文翻译自The Flask Mega-Tutorial Part V: User Logins 这是Flask Mega-Tutorial系列的第五部分,我将告诉你如何创建一个用户登录子系统. 你在 ...

  9. Format a Property Value 设置属性值的格式

    In this lesson, you will learn how to set a display format and an edit mask to a business class prop ...

  10. 【Untiy】完美解决Untiy Package Manager无限加载的问题

    直接上干货 打开记事本,复制一下zhei个 @echo offset HTTP_PROXY=127.0.0.1set HTTPS_PROXY=127.0.0.1start "" & ...