接上一篇《asp.net core 3.x 受權中的概念》,本篇看看asp.net core默認受權的流程。從兩個方面來看整個受權系統是怎麼運行的:啓動階段的配置、請求階段中間件的處理流程。html
因爲asp.net core 3.x目前使用終結點路由,所以受權框架能夠用於全部asp.net web項目類型,好比:webapi mvc razorpages...。但本篇只以MVC爲例ios
主要體現爲3點git
在mvc項目Startup.ConfigreServices中services.AddControllersWithViews(); (MvcServiceCollectionExtensions)用來向依賴注入框架註冊各類mvc相關服務。其中會調用services.AddAuthorization(選項)擴展方法(PolicyServiceCollectionExtensions)註冊受權相關服務,此擴展方法內部還會調用兩個擴展方法,這裏再也不深刻。github
這裏主要須要搞懂2個問題:web
AddAuthorization擴展方法的參數是Action<AuthorizationOptions>類型的,這是asp.net core中典型的選項模型,未來某個地方須要時,直接注入此選項對象,那時依賴注入容器會使用此委託對這個選項對象賦值。因此咱們在啓動時能夠經過此對象來對受權框架進行配置。api
最最重要的是咱們能夠在這裏配置全局受權策略列表,參考上圖的右側中間部分,源碼很少,注意註釋。cookie
1 //表明受權系統的全局選項對象,裏面最最核心的就是存儲着全局受權策略 2 public class AuthorizationOptions 3 { 4 //存儲全局受權策略(AuthorizationPolicy),key是策略惟一名,方便未來獲取 5 private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase); 6 //受權驗證時,將遍歷全部受權處理器(Authorization)逐個進行驗證,若某個發生錯誤是否當即終止後續的受權處理器的執行 7 public bool InvokeHandlersAfterFailure { get; set; } = true; 8 //默認受權策略,拒絕匿名訪問 9 public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); 10 //若未來受權檢查時沒有找到合適的受權策略,默認受權策略也是空的狀況下會回退使用此策略 11 public AuthorizationPolicy FallbackPolicy { get; set; } 12 //添加全局策略 13 public void AddPolicy(string name, AuthorizationPolicy policy) 14 { 15 if (name == null) 16 { 17 throw new ArgumentNullException(nameof(name)); 18 } 19 20 if (policy == null) 21 { 22 throw new ArgumentNullException(nameof(policy)); 23 } 24 25 PolicyMap[name] = policy; 26 } 27 //添加全局策略,同時能夠對此策略進行配置 28 public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy) 29 { 30 if (name == null) 31 { 32 throw new ArgumentNullException(nameof(name)); 33 } 34 35 if (configurePolicy == null) 36 { 37 throw new ArgumentNullException(nameof(configurePolicy)); 38 } 39 40 var policyBuilder = new AuthorizationPolicyBuilder(); 41 configurePolicy(policyBuilder); 42 PolicyMap[name] = policyBuilder.Build(); 43 } 44 //獲取指定名稱的策略 45 public AuthorizationPolicy GetPolicy(string name) 46 { 47 if (name == null) 48 { 49 throw new ArgumentNullException(nameof(name)); 50 } 51 52 return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null; 53 } 54 }
舉個栗子:mvc
1 services.AddControllersWithViews(); 2 services.AddRazorPages(); 3 services.AddAuthorization(opt => 4 { 5 opt.AddPolicy("受權策略1", builer => { 6 builer.RequireRole("admin", "manager"); 7 builer.AddAuthenticationSchemes("cookie", "google"); 8 //繼續配置.... 9 }); 10 opt.AddPolicy("受權策略2", builer => { 11 builer.RequireRole("teacher"); 12 builer.AddAuthenticationSchemes("wechat", "qq"); 13 //繼續配置.... 14 }); 15 });
這些接口都是擴展點,就問你怕不怕?固然絕大部分時候咱們不用管,默認的就足夠用了。app
主要注意的位置的爲題,必須在路由和身份驗證以後。框架
1 app.UseRouting(); 2 app.UseAuthentication(); 3 app.UseAuthorization();
擴展方法內部註冊了AuthorizationMiddleware
若是你對mvc稍有經驗,就曉得在一個Action上使用[Authorize]就能夠實施受權,如今咱們假設咱們在默認mvc項目中的HomeController定義以下Action,並應用受權標籤
1 [Authorize(Policy = "p1")]//使用全局受權策略中的"p1"進行受權判斷 2 [Authorize(AuthenticationSchemes = "google")]//只容許使用google身份驗證登陸的用戶訪問 3 [Authorize(Roles = "manager")]//只容許角色爲manager的訪問 4 public IActionResult Privacy() 5 { 6 return View(); 7 }
核心步驟以下:
步驟一、2得益於asp.net core 3.x的終結點路由,咱們能夠在進入MVC框架前就拿到Action及其之上應用的各類Atrribute,從而獲得咱們對當前受權策略定製所須要的數據
步驟3會根據獲得IAuthorizeData集合(AuthorizeAttribute實現了IAuthorizeData,它再也不是一個過濾器)建立當前準備用來作受權的策略。受權策略中 「身份驗證方案列表」 和 「受權依據列表」,就是經過這裏的標籤來的。具體來講:
這些Attribute能夠應用屢次,最終都是來定製當前受權策略的。後續步驟都會依賴此受權策略。
步驟4中,若發現本次受權策略中定義了多個身份驗證方案,則會注意進行身份驗證,獲得的多張證件會合併到當前用戶HttpContext.User中,固然默認身份驗證獲得的用戶信息也在其中。
上面步驟四、6是委託策略評估器PolicyEvaluator來完成的,往下看..
核心任務就兩個,身份驗證、進行受權
若策略沒有設置AuthenticationSchemes,則只判斷下當前請求是否已作身份驗證,若作了就返回成功
若策略設置了AuthenticationSchemes,則遍歷身份驗證方案逐個進行身份驗證處理 context.AuthenticateAsync(scheme); ,將全部獲得的用戶標識重組成一個複合的用戶標識。
調用IAuthorizationService進行權限判斷,若成功則返回成功。
不然 若身份驗證經過則 PolicyAuthorizationResult.Forbid() 直接通知身份驗證方案,作拒絕訪問處理;不然返回質詢
因此受權檢查的任務又交給了受權服務AuthorizationService
核心步驟以下:
步驟2還會判斷AuthorizationOptios.InvokeHandlersAfterFailure,來決定當某個處理器發生錯誤時,是否中止執行後續的受權處理器。
前面講過,默認只註冊了一個PassThroughAuthorizationHandler受權處理器,它會遍歷當前受權策略中實現了IAuthorizationHandler的受權依據(意思說這些對象既是受權處理器,也是受權依據)。直接執行它們。
public class PassThroughAuthorizationHandler : IAuthorizationHandler { public async Task HandleAsync(AuthorizationHandlerContext context) { foreach (var handler in context.Requirements.OfType<IAuthorizationHandler>()) { await handler.HandleAsync(context); } } }
以基於角色的受權依據RolesAuthorizationRequirement爲例,它繼承於AuthorizationHandler<RolesAuthorizationRequirement>,且實現IAuthorizationRequirement
1 public class RolesAuthorizationRequirement : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationRequirement 2 { 3 //省略部分代碼... 4 public IEnumerable<string> AllowedRoles { get; } 5 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement) 6 { 7 if (context.User != null) 8 { 9 bool found = false; 10 if (requirement.AllowedRoles == null || !requirement.AllowedRoles.Any()) 11 { 12 // Review: What do we want to do here? No roles requested is auto success? 13 } 14 else 15 { 16 found = requirement.AllowedRoles.Any(r => context.User.IsInRole(r)); 17 } 18 if (found) 19 { 20 context.Succeed(requirement); 21 } 22 } 23 return Task.CompletedTask; 24 } 25 }
能夠感覺到asp.net core 3.x目前的權限設計棒棒噠,默認的處理方式已經能知足大部分需求,即便有特殊需求擴展起來也很是簡單,前面註冊部分看到註冊了各類服務,且都有默認實現,這些服務在受權檢查的不一樣階段被使用,若是有必要咱們能夠自定義實現某些接口來實現擴展。本篇按默認流程走了一波,最好先看前一篇。這時候再去看官方文檔或源碼應該更容易。平常開發使用其實參考官方文檔就足夠了,無非就是功能權限和數據權限,看狀況也許不會寫後續的應用或擴展篇了。