.NET MVC5簡介(四)Filter和AuthorizeAttribute許可權驗證

来源:https://www.cnblogs.com/taotaozhuanyong/archive/2019/09/23/11575590.html
-Advertisement-
Play Games

在webform中,驗證的流程大致如下圖: 在AOP中: 在Filter中: AuthorizeAttribute許可權驗證 登錄後有許可權控制,有的頁面是需要用戶登錄才能訪問的,需要在訪問頁面增加一個驗證,也不能每個action都一遍。 1、寫一個CustomAuthorAttribute,繼承自Au ...


在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;
        }
View Code

全局異常處理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 = 0;
     public LimitActionFilterAttribute(int max = 1000)
     {
         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時再詳細介紹。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、題目 二、思路 1、dfs 實驗要求用多種思路完成,所以一開始就沿用了上一個實驗馬走棋盤的思路,添加了鄰接矩陣來記錄有向網的權值。總體思路還是DFS遍歷搜索。 過程剪枝: 1、因為要求為最短路徑,而一般情況總會存在多條可行路徑,在判斷過程中需要走過每一條路徑才能知道該路徑的長度,但如果已知一條可 ...
  • [TOC] 閉包函數 什麼是閉包函數 閉包函數把 閉包函數內的變數 + 閉包函數內部的函數, 這兩者包裹起來,然後通過返回值的形式返回出來。 定義在函數的內函數 該函數體代碼包含對該函數外層作用域中變數的引用 函數外層指的不是全局作用域 上述代碼中,f是一個全局的名字,但f拿到了inner的記憶體地址 ...
  • 我是一個2019畢業的非電腦的畢業生,從大二開始喜歡上Java直到現在一直都在學習,Brid從小就對電腦感興趣,可惜高中的時候不懂事,沒有規劃未來,考上了一所專科學院,然後大一併不能轉專業,現在畢業了沒有找到Java應屆的工作,只能找點其他的做,但是這阻住不了我對Java的喜歡,趁現在工作的晚上 ...
  • “容器”這兩個字很少被 Python 技術文章提起。一看到“容器”,大家想到的多是那頭藍色小鯨魚:Docker,但這篇文章和它沒有任何關係。本文里的容器,是 Python 中的一個抽象概念,是對專門用來裝其他對象的數據類型的統稱。 在 Python 中,有四類最常見的內建容器類型: 列表(list) ...
  • 溫馨提示 請收藏再看。此文篇幅太長,你短時間看不完;此文乾貨太多,錯過太可惜。 示例代碼可以關註 (公眾號)回覆 獲取。 收穫 1. 講解詳細:能讓你掌握使用 及類似校驗工具的各種使用姿勢 2. 內容全面:可以當做知識字典來查詢 what 註意:hibernate validator 與 持久層框架 ...
  • 閑及無聊 又打開了CSDN開始看一看有什麼先進的可以學習的相關帖子,這時看到了一位大神寫的簡歷裝X必備,手寫Spring MVC。 我想這個東西還是有一點意思的 就拜讀了一下大佬的博客 通讀了一遍相關代碼 感覺和我想象中spring的運作流程基本相同 但是我腦海中基本上只有一個非常簡單的基本概念 而 ...
  • 多好,多簡單,多好 ...
  • 一、題目 設平面上分佈著n個白點和n個黑點,每個點用一對坐標(x, y)表示。一個黑點b=(xb,yb)支配一個白點w=(xw, yw)當且僅當xb>=xw和yb>=yw。 若黑點b支配白點w,則黑點b和白點w可匹配(可形成一個匹配對)。 在一個黑點最多只能與一個白點匹配,一個白點最多只能與一個黑點 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...