在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. React: React组件的生命周期

    一.简介 在前面的第二篇博文中对组件的生命周期虽然做了一个大略介绍,但总感觉说的过于简单,毕竟生命周期是React组件的核心部分.在我们熟练使用React挂载和合成组件来创建应用表现层的过程中,针对数 ...

  2. JQ的offset().top与JS的getBoundingClientRect区别详解,JS获取元素距离视窗顶部可变距离

     壹 ❀ 引 我在 JQ的offset().top与js的offsetTop区别详解 这篇博客中详细分析了JQ方法offset().top与JS属性offsetTop的区别,并得出了一条offset( ...

  3. Latex学习与使用

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

  4. C sharp #002# 结构化编程基础

    饮水思源:金老师的自学网站.C# Guide 索引 变量与数据类型 C#中For each的写法 C#控制台程序编程技巧 简易图片浏览器 BigInteger以及浮点数的比较 一.变量与数据类型 us ...

  5. Java题库——chapter7 多维数组

    1)Which of the following statements are correct? 1) _______ A)char[ ][ ] charArray = {{'a', 'b'}, {' ...

  6. DevExpress的进度条控件ProgressBarControl的使用-以ZedGraph添加曲线进度为例

    场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...

  7. JS基础语法---函数作为参数使用---回调函数

    1. 函数可以作为参数使用, 如果一个函数作为参数, 那么我们说这个参数(函数)可以叫回调函数 2. 只要是看到一个函数作为参数使用了, 那就是回调函数 function sayHi(fn) { co ...

  8. 中文版 Apple 官方 Swift 教程《The Swift Programming Language》

    简介 欢迎使用 Swift 关于 Swift 版本兼容性 Swift 初见 Swift 版本历史记录 Swift 教程 基础部分 基本运算符 字符串和字符 集合类型 控制流 函数 闭包 枚举 类和结构 ...

  9. Flask报如下错误:SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.

    在同一个项目中由于flask_sqlalchemy版本不同,有时会报如下错误 错误信息如下: flask_sqlalchemy\__init__.py:: UserWarning: SQLALCHEM ...

  10. java 反编译工具 jd-gui

    下载地址    http://java-decompiler.github.io/         一般使用windows 版本  看你使用的操作系统了 解压  点击exe 进入 选择你编译后的cla ...