ASP.NET Core MVCフィルタ


ASP.NET CoreのフィルタASP.NET Core短絡ASPのキャンセルと設定NET MVC 4カスタムアクションフィルタ【旧バージョン-MVC 3およびMVC 4】
操作フィルタ(C#)【旧バージョン-MVC 1とMVC 2】Filters in ASPについてNET Core sample  
MVCフィルタメッセージブロック
.NET Core OAuth IdentityServer4 Token 
認証とASP.NET Web APIにおける授権Web API異常フィルタWeb API認証フィルタWeb API認証フィルタASPを実現する.NET Web APIセキュリティフィルタASP.NET CoreのクライアントIPセキュリティ(IPセキュリティリスト)方式(ミドルウェアAdminSafeListMiddleware、フィルタClientIpCheckFilter)検証失敗エラー応答モデル検証失敗文書ディレクトリ.NETドキュメントASP.NETドキュメント1、フィルタ:認証フィルタ:認証要求や認証要求のプロパティなど、実行する操作方法を安全に決定します.≪アクション・フィルタ|Action Filter|oem_src≫:パッケージ・アクション・メソッドの実行に使用します.このフィルタは、操作メソッドに追加のデータを入力したり、戻り値をチェックしたり、実行しない操作メソッド結果フィルタ:パッケージ用ActionResultオブジェクトの実行など、他の処理を実行できます.このフィルタは、HTTP応答の変更など、他の処理の結果を実行できます.≪例外フィルタ|Exception Filter|emdw≫:操作方法で、認可フィルタを使用して結果と実行を開始および終了した場所で未処理の例外が発生しなかった場合に実行されます.例外フィルタは、ログの記録やエラー・ページの表示などのタスクに使用できます.
認証フィルタ-IAuthenticationFilter認証フィルタ-IAuthorizationFilterプロパティを実装します.(またはAuthorizeAttribute)アクションフィルタ-IActionFilterプロパティを実装します.(またはActionFilterAttribute)結果フィルタ-IresultFilterプロパティを実装します.(またはActionFilterAttribute)例外フィルタ-IExceptionFilterプロパティを実装します.(またはHandleErrorAttribute)
2、フィルタ
ActionFilterAttribute:すべての属性フィルタのベースクラス[ActionFilterAttribute Class]OnActionExecuting(ActionExecutingContext filterContext)-コントローラ操作の「前」を実行すると、このメソッドOnActionExecuted(ActionExecutedContext filterContext)-コントローラ操作の「後」を実行すると、このメソッドが呼び出されます.OnResultExecuting(ResultExecutingContext filterContext):–このメソッドを呼び出すコントローラの操作結果を実行する前にOnResultExecuted(ResultExecutedContext filterContext):このメソッドを呼び出す後にコントローラの操作結果を実行します.
AuthorizeAttribute:コントローラまたは操作方法へのアクセスを指定するのは、許可要件を満たすユーザ(AuthorizeAttribute Class)AuthorizeCore(HttpContextBase)-書き換えられたときにカスタム認証チェックにエントリポイントを提供する.HandleUnauthorizedRequest(AuthorizationContext)-承認に失敗したHTTPリクエストを処理します.OnAuthorization(AuthorizationContext)-プロセスが承認を要求すると呼び出されます.OnCacheAuthorization(HttpContextBase)-キャッシュモジュールが許可を要求するときに呼び出す.
HandleErrorAttribute:操作方法による異常を処理するための属性(HandleErrorAttribute Class)OnException(ExceptionContext)-異常発生時に呼び出す.
3、承認フィルタ-AuthorizeAttribute(例)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace HappyShop.Web.Filters
{
    public class CustomerAuthorizeAttribute : AuthorizeAttribute
    {
        public new string[] Roles { get; set; }
        private HttpRequestBase _request;

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            _request = httpContext.Request;
            if (httpContext == null && httpContext.Session == null)
                throw new ArgumentNullException("HttpContext");
            
            if (!httpContext.User.Identity.IsAuthenticated)
                return false;

            if (!httpContext.Request.Cookies.AllKeys.Contains("CookieKey"))
                return false;

            var cookie = httpContext.Request.Cookies["CookieKey"];
            if (string.IsNullOrEmpty(cookie?.Value))
            {
                return false;
            }

            if (Roles == null)
            {
                return true;
            }
            if (Roles.Length == 0)
            {
                return true;
            }
            if (Roles.Any(httpContext.User.IsInRole))
            {
                return true;
            }
            return false;
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var httpContext = filterContext.HttpContext;
            _request = httpContext.Request;

            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            string actionName = filterContext.ActionDescriptor.ActionName;
            string roles = "1,2,3";
            if (!string.IsNullOrWhiteSpace(roles))
            {
                this.Roles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }
            base.OnAuthorization(filterContext);
        }
    }
}

4、操作フィルタ-ActionFilterAttribute(例)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace HappyShop.Web.Filters
{
    public class LogActionFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            var action = filterContext.ActionDescriptor.ActionName;
            Log("OnActionExecuting", filterContext.RouteData);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            Log("OnActionExecuted", filterContext.RouteData);
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            Log("OnResultExecuting", filterContext.RouteData);
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            Log("OnResultExecuted", filterContext.RouteData);
        }

        private void Log(string methodName, RouteData routeData)
        {
            var controllerName = routeData.Values["controller"];
            var actionName = routeData.Values["action"];
            var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
            Console.WriteLine(message, "Action Filter Log");
        }

    }
}

5、例外フィルタ-HandleErrorAttribute(例)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace HappyShop.Web.Filters
{
    public class CustomerExceptionAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            base.OnException(filterContext);

            filterContext.ExceptionHandled = true;

            var url = filterContext.RequestContext.HttpContext.Request.Url.AbsolutePath;

            Exception ex = filterContext.Exception;
            WriteLog(ex);

            ///   
            filterContext.Result = new RedirectResult("~/Shared/Error");

            ///   
            filterContext.Result = new ContentResult()
            {
                Content = JsonConvert.SerializeObject(new
                {
                    Success = false,
                    Msg = $"--url:{DateTime.Now:HH:mm:ss.fff};       "
                })
            };
        }

        private void WriteLog(Exception exception)
        {
            //...
        }
    }
}

Web APIを実現する認証フィルタはWeb APIにおいて、認証フィルタはSystemを実現する.Web.Http.Filters.IAuthenticationFilterインタフェース.システムから引き継ぐAttributeは、属性として適用されます.IAuthenticationFilterインタフェースには2つの方法があります.AuthenticateAsyncリクエストは、認証証明書がリクエストに存在する場合、認証証明書を介して認証されます.ChallengeAsyncはHTTP応答に認証質問を追加する必要があります.**-----------------------ASP.NET Coreフィルタ------------------------------操作フィルタASP.NET Core 3.0アクションフィルタは、IActionFilterまたはIAsyncActionFilterインタフェースを実装します.ショートを設定するには、Microsoftを使用します.AspNetCore.Mvc.Filters.ActionExecutingContext.Resultは結果インスタンスに割り当てられ、next(ActionExecutionDelegate)は呼び出されません.このフレームワークは、サブクラス化可能な抽象ActionFilterAttributeを提供する.OnActionExecutingアクションフィルタは、モデルのステータスを検証するために使用できます.ステータスが無効な場合は、エラーが返されます.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }

        var request = context.HttpContext.Request;
        var headers = request.Headers;
        var origins = Configuration["AppSettings:Origins"].Split(',');
        var origin = context.HttpContext.Request.Headers["Origin"].FirstOrDefault();
        var host = context.HttpContext.Request.Headers["Host"].FirstOrDefault();
        var cookie = context.HttpContext.Request.Headers["Cookie"].FirstOrDefault();
        string referer = context.HttpContext.Request.Headers["Referer"].ToString();
        var list = referer.Split("/").Where(x => !string.IsNullOrEmpty(x) && !x.Contains("v") && !x.Contains("api")).ToArray();

        if (!origins.Any(x => x == origin) && !origins.Any(x => x == host))
        {
            context.Result = new JsonResult(new { code = "401", msg = "         "
        });
            return;
        }
        if (list != null && list.Length > 0)
        {
            if (!origins.Any(x => x == list[1]))
            {
                context.Result = new JsonResult(new { code = "401", msg = "         " });
                return;
            }
        }
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Headerパラメータの取得
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var headers = context.HttpContext.Request.Headers;
        var userId = headers["use_id"].FirstOrDefault().ToLower();

        var isDefined = false;
        var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        if (controllerActionDescriptor != null)
        {
            isDefined = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
              .Any(a => a.GetType().Equals(typeof(NoPermissionAttribute)));
        }
        if (isDefined) return;
    }
}

ASP.NET Core 3.0ショートカットのキャンセルと設定フィルタメソッドに提供されるResourceExecutingContextパラメータのResultプロパティを設定することで、フィルタパイプをショートさせることができます.たとえば、次のリソースフィルタは、パイプの残りのフェーズを実行できません.
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult()
        {
            Content = "Resource unavailable - header not set."
        };
    }
}

次のアクション・フィルタはショートを設定し、パイプの残りのフェーズを実行できません.
public class PermissionAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["LoginInfo"].ToString()))
        {
            context.Result = new JsonResult(new { Code = EnumCode.UnAuthorized, Message = "    " });
        }
        base.OnActionExecuting(context);
    }
}

7、AuthorizeFilterは[Authorize]特性を追加したかどうかを判断し、通常はリソースサーバのフィルタに追加する
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace Test.WebAPI
{
    public class TestAuthorizeAttribute : AuthorizeFilter
    {
        private static AuthorizationPolicy _policy_ = new AuthorizationPolicy(
            new[] { new DenyAnonymousAuthorizationRequirement() },
            new string[] { });

        public TestAuthorizeAttribute () : base(_policy_) { }

        /// 
        ///      ,            ,ExceptionFilter    
        /// 
        ///       
        public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            await base.OnAuthorizationAsync(context);

            if (!context.HttpContext.User.Identity.IsAuthenticated ||
                 context.Filters.Any(item => item is IAllowAnonymousFilter)) return;

            var request = context.HttpContext.Request;
            var headers = request.Headers;
            var deviceId = headers["device_id"].FirstOrDefault().ToLower();

            if (context.HttpContext.User.Identity.IsAuthenticated)
            {
                var claimIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;
                Claim userClaim = claimIdentity.Claims.Where(x => x.Type.Contains("user_code")).FirstOrDefault();
                Claim userClaim = claimIdentity.Claims.Where(x => x.Type.Contains("device_id")).FirstOrDefault();

                if (userClaim.Value != deviceId)
                {
                    context.Result = new JsonResult(new { code = ((int)EnumCode.UnAuthorized).ToString(), msg = "         " });
                    return;
                }

                if (IsHaveAllow(context.Filters))
                {
                    return;
                }

                //  url
                // {/ Home / Index}
                var originalUrl = context.HttpContext.Request.Path.Value;
                if (string.IsNullOrWhiteSpace(originalUrl))
                {
                    return;
                }

                var list = originalUrl.Split("/");
                var liit = originalUrl.Split("/").Where(x => !x.Contains("v") && !x.Contains("api")).ToArray();
                var controllerName = list[1].ToString().Trim();
                var actionName = list[2].ToString().Trim();
                if (list.Length <= 0 || url == "/")
                {
                    return;
                }

                //  
                var flag = IsHavePower(controllerName, actionName);
                if (flag != 0)
                {
                    context.Result = new RedirectResult("/Home/Index");
                }
            }
            if (context.HttpContext.User.Identity.Name != "admin")
            {
                //               
                RedirectToActionResult content = new RedirectToActionResult("NoAuth", "Exception", null);
                context.Result = content;
            }

            ///                
            if (string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["LoginInfo"].ToString()))
            {
                //context.Result = new JsonResult(new { code = ((int)EnumCode.UnAuthorized).ToString(), msg = "    " });
            }
        }

        /// 
        ///          
        /// 
        /// 
        /// 
        public static bool IsHaveAllow(IList filers)
        {
            for (int i = 0; i < filers.Count; i++)
            {
                if (filers[i] is IAllowAnonymousFilter)
                {
                    return true;
                }
            }
            return false;
        }

        public static int IsHavePower(string controllerName, string actionName)
        {
            return 0;
        }

    }

    public class AllowAnonymous : AuthorizeFilter, IAllowAnonymousFilter
    {

    }

    public class PermissionRequiredAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
        }
    }


    public class PermissionHandler : IAuthorizationHandler
    {
        public Task HandleAsync(AuthorizationHandlerContext context)
        {
            var pendingRequirements = context.PendingRequirements.ToList();

            foreach (var requirement in pendingRequirements)
            {
            }

            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        private bool IsOwner(ClaimsPrincipal user, object resource)
        {
            // Code omitted for brevity

            return true;
        }

        private bool IsSponsor(ClaimsPrincipal user, object resource)
        {
            // Code omitted for brevity

            return true;
        }
    }
}

********サンプル************