Talk about Asp. net filter Filter
- 2021-11-13 07:16:06
- OfStack
Recently, when sorting out and optimizing. net code, I found several unfriendly processing phenomena: login judgment, permission authentication, logging, exception handling and other common operations, which are everywhere in action in the project. In code optimization, this 1 point is a very important focus. At this time, the filter and interceptor (Filter) in net will come in handy. Now, according to the actual work in the past few days, I have made a simple comb and shared it for everyone's reference and communication. If there is anything wrong with writing, I will point out and communicate more.
Overview:
Filter in. net mainly includes the following four categories: Authorize (authorization), ActionFilter (custom) and HandleError (error handling).
过滤器 |
类名 |
实现接口 |
描述 |
授权 |
AuthorizeAttribute |
IAuthorizationFilter |
此类型(或过滤器)用于限制进入控制器或控制器的某个行为方法,比如:登录、权限、访问控制等等 |
异常 |
HandleErrorAttribute |
IExceptionFilter |
用于指定1个行为,这个被指定的行为处理某个行为方法或某个控制器里面抛出的异常,比如:全局异常统1处理。 |
自定义 |
ActionFilterAttribute |
IActionFilter和IResultFilter |
用于进入行为之前或之后的处理或返回结果的之前或之后的处理,比如:用户请求日志详情日志记录 |
AuthorizeAttribute: Authentication and Authorization
Authentication authorization is mainly to authenticate the first entrance of all action access, and to do the first supervision filter interception gate for user access.
Implementation: It is necessary to customize a class, inherit AuthorizeAttribute and rewrite OnAuthorization, and all Request information requested by users can be obtained in OnAuthorization. In fact, all the data support of all authentication interception operations we do comes from Request.
Specific verification process design:
IP White List: This is mainly for API to do IP restriction, only specified IP can be accessed, and non-specified IP can be returned directly
Request frequency control: This is mainly to control the user's access frequency, mainly for API, beyond the request frequency to return directly.
Login authentication: Login authentication 1 is verified by passing token in the requested header, so that MVC login authentication with 1 is used, and Auth authentication with API interface is also used, and it does not depend on the user's front-end js settings.
Authorization authentication: Authorization authentication is simple, mainly to verify whether the user has the authority, if not, directly do the corresponding return processing.
Similarities and differences between MVC and API:
Namespace: MVC: System. Web. Http. Filters; API: System. Web. Mvc
Injection mode: In injection mode, it mainly includes: global- > Controller Controller- > Behavior Action
Global registration: All Aciton for all systems use
Controller: Only works for Action under this Controller
Action: Only works for this Action
Among them, there are one difference between MVC and API for global registration:
MVC is injected into FilterConfig. cs
filters.Add(new XYHMVCAuthorizeAttribute());
API is injected into WebApiConfig. cs
config.Filters.Add(new XYHAPIAuthorizeAttribute());
Note: In actual use, for authentication authorization, we generally add global authentication, but some action does not need authentication, such as the original login Action, etc., so how to exclude it? In fact, it is also very simple. We only need to define an Attribute integrated Attribute or an AllowAnonymousAttribute of the system by ourselves. In the action that does not need verification, we only need to register the corresponding Attribute and do a filter before verification, such as:
// Have AllowAnonymous The interface of the property directly turns on the green light
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
{
return;
}
API AuthFilterAttribute instance code
/// <summary>
/// Authorization authentication filter
/// </summary>
public class XYHAPIAuthFilterAttribute : AuthorizationFilterAttribute
{
/// <summary>
/// Authentication authorization verification
/// </summary>
/// <param name="actionContext"> Request context </param>
public override void OnAuthorization(HttpActionContext actionContext)
{
// Have AllowAnonymous The interface of the property directly turns on the green light
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
{
return;
}
// Do it before request 1 Layer interception, main verification token Validity and verification of
HttpRequest httpRequest = HttpContext.Current.Request;
// Get apikey
var apikey = httpRequest.QueryString["apikey"];
// Do it first IP Whitelist check
MBaseResult<string> result = new AuthCheckService().CheckIpWhitelist(FilterAttributeHelp.GetIPAddress(actionContext.Request), apikey);
// Inspection time rub
string timestamp = httpRequest.QueryString["Timestamp"];
if (result.Code == MResultCodeEnum.successCode)
{
// Inspection time rub
result = new AuthCheckService().CheckTimestamp(timestamp);
}
if (result.Code == MResultCodeEnum.successCode)
{
// Do request frequency verification
string acitonName = actionContext.ActionDescriptor.ActionName;
string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
result = new AuthCheckService().CheckRequestFrequency(apikey, $"api/{controllerName.ToLower()}/{acitonName.ToLower()}");
}
if (result.Code == MResultCodeEnum.successCode)
{
// Signature verification
// Get all request parameters
Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();
result = new AuthCheckService().SignCheck(queryParameters, apikey);
if (result.Code == MResultCodeEnum.successCode)
{
// If there is NoChekokenFilterAttribute Label Then don't do it directly token Certification
if (actionContext.ActionDescriptor.GetCustomAttributes<XYHAPINoChekokenFilterAttribute>().Any())
{
return;
}
// Calibration token Validity of
// Get 1 A token
string token = httpRequest.Headers.GetValues("Token") == null ? string.Empty :
httpRequest.Headers.GetValues("Token")[0];
result = new AuthCheckService().CheckToken(token, apikey, httpRequest.FilePath);
}
}
// Output
if (result.Code != MResultCodeEnum.successCode)
{
// 1 Be sure to instantiate 1 A response, Will it eventually be executed action Code in
actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
// You need to specify your own output content and type
HttpContext.Current.Response.ContentType = "text/html;charset=utf-8";
HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result));
HttpContext.Current.Response.End(); // End the response here, and it will not walk by the system
}
}
}
MVC AuthFilterAttribute instance code
/// <summary>
/// MVC自定义授权
/// 认证授权有两个重写方法
/// 具体的认证逻辑实现:AuthorizeCore 这个里面写具体的认证逻辑,认证成功返回true,反之返回false
/// 认证失败处理逻辑:HandleUnauthorizedRequest 前1步返回 false时,就会执行到该方法中
/// 但是,我平时在应用过程中,1般都是在AuthorizeCore根据不同的认证结果,直接做认证后的逻辑处理
/// </summary>
public class XYHMVCAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// 认证逻辑
/// </summary>
/// <param name="filterContext">过滤器上下文</param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
// 此处主要写认证授权的相关验证逻辑
// 该部分的验证1般包括两个部分
// 登录权限校验
// --我们的1般处理方式是,通过header中传递1个token来进行逻辑验证
// --当然不同的系统在设计上也不尽相同,有的也会采用session等方式来验证
// --所以最终还是根据其项目本身的实际情况来进行对应的逻辑操作
// 具体的页面权限校验
// --该部分的验证是具体的到页面权限验证
// --我看有得小伙伴没有做到这1个程度,直接将这1步放在前端js来验证,这样不是很安全,但是可以拦住小白用户
// --当然有的系统根本就没有做权限控制,那就更不需要这1个逻辑了。
// --所以最终还是根据其项目本身的实际情况来进行对应的逻辑操作
// 现在用1个粗暴的方式来简单模拟实现过,用系统当前时间段秒厨艺3,取余数
// 当余数为0:认证授权通过
// 1:代表为登录,调整至登录页面
// 2:代表无访问权限,调整至无权限提示页面
// 当然,在这也还可以做1些IP白名单,IP黑名单验证 请求频率验证等等
// 说到这而,还有1点需要注意,如果我们选择的是全局注册该过滤器,那么如果有的页面根本不需要权限认证,比如登录页面,那么我们可以给不需要权限的认证的控制器或者action添加1个特殊的注解 AllowAnonymous ,来排除
// 获取Request的几个关键信息
HttpRequest httpRequest = HttpContext.Current.Request;
string acitonName = filterContext.ActionDescriptor.ActionName;
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
// 注意:如果认证不通过,需要设置filterContext.Result的值,否则还是会执行action中的逻辑
filterContext.Result = null;
int thisSecond = System.DateTime.Now.Second;
switch (thisSecond % 3)
{
case 0:
// 认证授权通过
break;
case 1:
// 代表为登录,调整至登录页面
// 只有设置了Result才会终结操作
filterContext.Result = new RedirectResult("/html/Login.html");
break;
case 2:
// 代表无访问权限,调整至无权限提示页面
filterContext.Result = new RedirectResult("/html/NoAuth.html");
break;
}
}
}
ActionFilter: Custom Filter
Custom filter, mainly monitoring action request before and after, processing results before and after the return of events. API has only two methods before and after the request.
重新方法 |
方法功能描述 |
使用于 |
OnActionExecuting |
1个请求在进入到aciton逻辑前执行 |
MVC、API |
OnActionExecuted |
1个请求aciton逻辑执行后执行 |
MVC、API |
OnResultExecuting |
对应的view视图渲染前执行 |
MVC |
OnResultExecuted |
对应的view视图渲染后执行 |
MVC |
Among these methods, we are mainly used to record the interaction log and record the time-consuming situation of each step, so as to optimize the use of the subsequent system. Specific use, according to their own business scenarios.
Among them, the similarities and differences between MVC and API are similar to the similarities and differences of authentication authorization mentioned above, so they are not described in detail.
The following 1 example code:
API Defines Filter Instance DEMO Code
/// <summary>
/// Action Filter
/// </summary>
public class XYHAPICustomActionFilterAttribute : ActionFilterAttribute
{
/// <summary>
/// Action Start of execution
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
}
/// <summary>
/// action After execution
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuted(HttpActionExecutedContext actionContext)
{
try
{
// Build 1 Log data model
MApiRequestLogs apiRequestLogsM = new MApiRequestLogs();
// API Name
apiRequestLogsM.API = actionContext.Request.RequestUri.AbsolutePath;
// apiKey
apiRequestLogsM.API_KEY = HttpContext.Current.Request.QueryString["ApiKey"];
// IP Address
apiRequestLogsM.IP = FilterAttributeHelp.GetIPAddress(actionContext.Request);
// Get token
string token = HttpContext.Current.Request.Headers.GetValues("Token") == null ? string.Empty :
HttpContext.Current.Request.Headers.GetValues("Token")[0];
apiRequestLogsM.TOKEN = token;
// URL
apiRequestLogsM.URL = actionContext.Request.RequestUri.AbsoluteUri;
// Return information
var objectContent = actionContext.Response.Content as ObjectContent;
var returnValue = objectContent.Value;
apiRequestLogsM.RESPONSE_INFOR = returnValue.ToString();
// Because the database can only store the maximum 4000 String, so do the return value 1 Interception
if (!string.IsNullOrEmpty(apiRequestLogsM.RESPONSE_INFOR) &&
apiRequestLogsM.RESPONSE_INFOR.Length > 4000)
{
apiRequestLogsM.RESPONSE_INFOR = apiRequestLogsM.RESPONSE_INFOR.Substring(0, 2000);
}
// Request parameter
apiRequestLogsM.REQUEST_INFOR = actionContext.Request.RequestUri.Query;
// Definition 1 Asynchronous delegates , Asynchronous logging
// Func<MApiRequestLogs, string> action = AddApiRequestLogs;// Declaration 1 Delegation
// IAsyncResult ret = action.BeginInvoke(apiRequestLogsM, null, null);
}
catch (Exception ex)
{
}
}
}
HandleError: Error Handling
Exception handling is very common for us, very good use of exception handling, can be very good to avoid the whole try/catch. Exception handling box is very simple, the value needs custom integration: ExceptionFilterAttribute, and custom implementation: OnException method can be.
In OnException, we can do some corresponding logical processing according to our own needs, such as recording exception logs, which is convenient for subsequent problem analysis and follow-up.
OnException also has a very important processing, that is, the exception of the results of the package, return a very friendly results to the user, to avoid some unnecessary information returned to the user. For example, for MVC, follow up different exceptions, adjust the system 1 to a friendly prompt page, and so on; For API, then we can return several encapsulations of one system 1, which is convenient for user system 1 to process the results.
Exception handling instance code for MVC:
/// <summary>
/// MVC Custom exception handling mechanism
/// When it comes to exception handling, in fact, the first in our mind 1 The reaction, too, should be try/cache Operation
/// However, in actual development, it is very likely that address errors will not enter at all try In, or not by try Handled to exception
/// This class plays a role, which can well handle uncaught exceptions and do corresponding logical processing
/// Custom exception mechanism, mainly integrating HandleErrorAttribute Rewrite its OnException Method
/// </summary>
public class XYHMVCHandleError : HandleErrorAttribute
{
/// <summary>
/// Handle exceptions
/// </summary>
/// <param name="filterContext"> Exception context </param>
public override void OnException(ExceptionContext filterContext)
{
// In our usual projects, exception handling 1 There are two functions in general
// 1: Detailed log of exceptions, which is convenient for post-analysis log
// 2: Unification of anomalies 1 Friendly handling, such as redirecting to a friendly prompt page according to the exception type
// Both unprocessed exception information and request information can be obtained in this system
// Here, you can do the corresponding logical processing according to the actual project needs
// Here is a brief list of several key ways to obtain information
// Controller name Note that what is obtained in this way is 1 Full path of files
string contropath = filterContext.Controller.ToString();
// Relative path to access directory
string filePath = filterContext.HttpContext.Request.FilePath;
// url Full address
string url = (filterContext.HttpContext.Request.Url.AbsoluteUri).ExUrlDeCode();
// Request mode post get
string httpMethod = filterContext.HttpContext.Request.HttpMethod;
// Request IP Address
string ip = filterContext.HttpContext.Request.GetIPAddress();
// Get all request parameters
HttpRequest httpRequest = HttpContext.Current.Request;
Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();
// Get exception object
Exception ex = filterContext.Exception;
// Exception description information
string exMessage = ex.Message;
// Exception stack information
string stackTrace = ex.StackTrace;
// Record logs according to the actual situation (text log, database log, it is recommended that the specific steps be completed asynchronously)
filterContext.ExceptionHandled = true;
// Simulation does corresponding logic processing according to different ones
int statusCode = filterContext.HttpContext.Response.StatusCode;
if (statusCode>=400 && statusCode<500)
{
filterContext.Result = new RedirectResult("/html/404.html");
}
else
{
filterContext.Result = new RedirectResult("/html/500.html");
}
}
}
Exception handling instance code for API:
/// <summary>
/// API Custom exception handling mechanism
/// When it comes to exception handling, in fact, the first in our mind 1 The reaction, too, should be try/cache Operation
/// However, in actual development, it is very likely that address errors will not enter at all try In, or not by try Handled to exception
/// This class plays a role, which can well handle uncaught exceptions and do corresponding logical processing
/// Custom exception mechanism, mainly integrating ExceptionFilterAttribute Rewrite its OnException Method
/// </summary>
public class XYHAPIHandleError : ExceptionFilterAttribute
{
/// <summary>
/// Handle exceptions
/// </summary>
/// <param name="actionExecutedContext"> Exception context </param>
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
// In our usual projects, exception handling 1 There are two functions in general
// 1: Detailed log of exceptions, which is convenient for post-analysis log
// 2: Unification of anomalies 1 Friendly handling, such as redirecting to a friendly prompt page according to the exception type
// Both unprocessed exception information and request information can be obtained in this system
// Here, you can do the corresponding logical processing according to the actual project needs
// Here is a brief list of several key ways to obtain information
// action Name
string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
// Controller name
string controllerName =actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName;
// url Full address
string url = (actionExecutedContext.Request.RequestUri.AbsoluteUri).ExUrlDeCode();
// Request mode post get
string httpMethod = actionExecutedContext.Request.Method.Method;
// Request IP Address
string ip = actionExecutedContext.Request.GetIPAddress();
// Get all request parameters
HttpRequest httpRequest = HttpContext.Current.Request;
Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();
// Get exception object
Exception ex = actionExecutedContext.Exception;
// Exception description information
string exMessage = ex.Message;
// Exception stack information
string stackTrace = ex.StackTrace;
// Record logs according to the actual situation (text log, database log, it is recommended that the specific steps be completed asynchronously)
// Logic of landing your own record log ......
// Constructing system 1 The internal exception handling mechanism of the exception is equivalent to doing 1 Stratigraphic series 1 Packaging exposure
MBaseResult<string> result = new MBaseResult<string>()
{
Code = MResultCodeEnum.systemErrorCode,
Message = MResultCodeEnum.systemError
};
actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
// You need to specify your own output content and type
HttpContext.Current.Response.ContentType = "text/html;charset=utf-8";
HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result));
HttpContext.Current.Response.End(); // End the response here, and it will not walk by the system
}
}
Summarize
. net filter, my personal understanding of one sentence is: to action each stage of the unified monitoring and processing operations. . net filter, where the order of execution of each filter is: Authorize (Authorization)-- > ActionFilter (custom)-- > HandleError (Error Handling)
Ok, let's talk about it first. If something is wrong, give more advice and forgive me. I wrote an exercise DEMO myself, which will have instructions for dealing with each situation. If you are interested, you can download it and have a look. Thank you.
The address of DEMO in GitHub is: https://github.com/xuyuanhong0902/XYH.FilterTest.git