.NET CORE 授權


.NET CORE 授權

一、三種方式授權

不論使用NET CORE框架的何種授權都必須引入中間件,因為它實現了在管道中對當前請求的鑒權和授權的驗證,在Startup中的Configure中首先加入鑒權和授權的中間件

中間件 描述
UseAuthentication 鑒權中間件
UseAuthorization 授權中間件,基於鑒權
1.Scheme 和 Role

基於Cookie 的Scheme授權,就是在授權時檢查下是否存在對應的Scheme,可以使用自定義的Scheme授權,當然此處只是簡單介紹SchemeRole授權的方法,在選擇使用.NET CORE框架的授權方式時,常用的是Policy的方式,因為Policy的擴展性和可配置性是三種方式中最強的,其實歸根結底SchemeRole授權方式實現的本質,就是基於Policy的封裝而已,至於是如何實現到后面結合源碼來探究,但是在這之前,要知道如何使用它

  • 1.引入中間件並且在IOC容器中注冊基於Scheme授權

    //在IOC中注冊
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
        options.LoginPath = "/Authorization/Index";
        options.AccessDeniedPath = "/Authorization/Index";
    });
    
    
    //在Configure中引入中間件
    app.UseAuthentication();
    app.UseAuthorization();
    
  • 2.在對應的API中加入授權特性,特性的策略名AuthenticationSchemes必須和注冊IOC的authenticationScheme一致

    [Authorize(AuthenticationSchemes = "Cookies")]
    public IActionResult Info()
    {
        return View();
    }
    
  • 3.如果使用角色授權,就需要在登錄時寫入Claim的Role信息,然后在對應的Api接口上使用時加入[Authorize]特性

    [AllowAnonymous]
    public IActionResult Login(string name, string password)
    {
    	//用戶名密碼不正確直接返回
        if (!"Admin".Equals(name) || !"123456".Equals(name))
        {
           return new JsonResult(new{ Result = false,Message = "登錄失敗" });
        }
    
    	var claimIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
    	//寫入身份信息角色為
    	claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
     	await base.HttpContext.SignInAsync("Cookies", new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
        {
               ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
        });
      	return new JsonResult(new{ Result = true,Message = "登錄成功"});
    }
    
    [Authorize(Roles = "Admin")]
    public IActionResult InfoAdmin()
    {
        return View();
    }
    
    
2.Policy
  • 1.在Ioc中注冊2種基於策略的授權AdminPolicy UserPolicy,在授權AdminPolicy時會校驗Claim的Role的值是“Admin”,Name的值是“美洋洋”,而且必須包含郵箱,在檢驗UserPolicy時會驗證Claim中是否存在Role這個Key,並且驗證Role的值是"User",如果用戶鑒權通過的信息中完全符合這些規則的要求,那么授權就會通過

     services.AddAuthorization(options =>
     {
         options.AddPolicy("AdminPolicy",
             policyBuilder => policyBuilder
             //Claim的Role是Admin
             .RequireRole("Admin")
             //Claim的Name是美洋洋
             .RequireUserName("美洋洋")
             ////必須有某個Cliam
             .RequireClaim(ClaimTypes.Email)
             );
         options.AddPolicy("UserPolicy",
             policyBuilder => policyBuilder.RequireAssertion(context =>
             context.User.HasClaim(c => c.Type == ClaimTypes.Role)
             && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "User")
        );
     });
    
  • 2.我們在控制器中,在需要使用授權的Action上,標記授權特性並聲明使用Policy的方式,當客戶端訪問Action 時管道會自動去檢驗它的合法性,確定是否授權,注意如果Action 上同時使用了[AllowAnonymous],那么就會忽略授權檢驗,因為[AllowAnonymous]的優先級是高於[Authorize]

    [Authorize(Policy = "AdminPolicy")]
    public IActionResult AdminPolicy()
    {
        return View();
    }
    

二、源碼解讀授權流程

  • 1.經過上面幾種簡單的配置基本知道了3種方式的使用方法,在我們去探究查看他的實現源碼之前,我們腦海中應該對它的流程有個大概的構思,如果是我們自己來做這個功能怎么做,一般不管有沒有工作經驗的朋友,基本都能想出解決方案,只是方案的細節差異或者邏輯嚴謹以及擴展度不同而已,大概的思路無非大同小異。

    • 1.如果是Policy方式提前定義好授權的規則信息,登錄成功后存儲用戶的各項身份信息。
    • 2.反射找到控制器上標記的Authorize特性。
    • 3.根據特性上給的授權方式和規則以及對應的值 與 用戶身份信息中的信息比對來得出是否能訪問當前控制器。
  • 2.根據上面4點簡單的邏輯可以實現授權,甚至可以說.NET CORE對於授權大致思路也是這么做的,只是他的擴展和可配置以及設計方式是經過詳細策划的,以至於實現的更加完整和合理,對於我們,主要思考的問題有如下幾點。

    • 1.什么時候注冊規則?能不能自定義規則?

    • 2.什么時候找到控制器標注的特性?

    • 3.什么時候對比規則決定是否通過授權?

接下來我們帶着我們自己假設性的思路及思考的問題,來查看源碼是如何做到的

1.注冊解析流程
1.授權注冊常用類
  • 1.首先我們需要簡單認識一下如下幾個類

    類名 描述
    PolicyServiceCollectionExtensions 用戶注冊授權服務的擴展類,包含2個AddAuthorization()方法
    AuthorizationOptions 用於添加授權時,自定義配置的提供類,他提供了自定義添加策略和查找策略的方法以及存儲的屬性
    AuthorizationPolicyBuilder 用於提供用戶配置添加授權規則,再根據規則和Scheme創建AuthorizationPolicy的功能
    AuthorizationPolicy AuthorizationPolicy的一個實例,對應一個授權的Policy,它是由AuthorizationPolicyBuilder創建
    IAuthorizationRequirement 提供授權規則的接口約束,他的實例Requirement就是具體的授權條件,例如Role授權方式的規則,由它的實例RolesAuthorizationRequirement實現
2.規則源碼解析
  • 1.在.NET CORE中授權注入規則是依賴PolicyServiceCollectionExtensions擴展類來完成的,他包含了2個重載的AddAuthorization(),它的第二個重載包含了一個帶參的AuthorizationOptions類型的無返回值委托,我們將從他開始介紹,如果看過WebApi的源碼應該知道,我們的Application_Start啟動類中,調用的GlobalConfiguration.Configure方法就是這樣的風格及設計,至於如果不知道為何使用這種設計方式的話,應該是沒有搞清楚委托的本質,建議去真正的理解了委托之后再來看,因為委托單單對於NET CORE來說是功不可沒的。

  • 2.打開AuthorizationOptions里面,包含了一個值為AuthorizationPolicy類型的字典PolicyMap和重載的AddPolicy()方法,我們在注冊階段主要介紹PolicyMapAddPolicy()的第二個重載方法,而PolicyMap就是用於存儲Policy的,我們看到同樣在AddPolicy()時,也將一個無返回值委托作為第二個參數,而委托的參數為AuthorizationPolicyBuilder類型的,而AuthorizationPolicyBuilder的最終目的,就是用來創建AuthorizationPolicy和創建規則的。

  • 3.打開AuthorizationPolicyBuilder類,有一個IList 類型的Requirements屬性,他是用於存儲用戶注冊是添加的規則集合,現在看到配置策略的這段代碼是不是瞬間就明白了。

      1. AuthorizationOptions 類中的第二個AddPolicy()方法用於添加一個Policy
      1. AuthorizationPolicyBuilder 類中RequireUserName()方法用於添加規則,最終加入到AuthorizationPolicyBuilder的Requirements屬性中去了
    services.AddAuthorization((AuthorizationOptions authorizationOptions) =>
    {
        authorizationOptions.AddPolicy("CustomPolicy",
            (AuthorizationPolicyBuilder authorizationPolicyBuilder) => 
              authorizationPolicyBuilder.RequireUserName("懶洋洋"));
    })
    

  • 4.我們看到AuthorizationPolicyBuilder的RequireUserName()方法,他加入的是一個繼承自IAuthorizationRequirement接口的NameAuthorizationRequirement,它的作用就是一個驗證規則,同理我們根據這個思想,完全可以擴展自己的規則。

3.擴展IAuthorizationRequirement
  • 1.創建一個手機號的校驗規則,如果以180和139開頭的手機號,那么就能訪問某一個Api

    public class MobilePhoneRequirement : AuthorizationHandler<MobilePhoneRequirement>, IAuthorizationRequirement
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MobilePhoneRequirement requirement)
        {
            if (context.User != null && context.User.HasClaim(c => c.Type == ClaimTypes.MobilePhone))
            {  
                var phone = context.User.FindFirst(c => c.Type == ClaimTypes.MobilePhone).Value;
                if (phone.StartsWith("180", StringComparison.OrdinalIgnoreCase) || email.StartsWith("139", StringComparison.OrdinalIgnoreCase))
                {
                    context.Succeed(requirement);
                }
                else
                {
                    //context.Fail();//沒成功就留給別人處理
                }
            }
            return Task.CompletedTask;
        }
    }
    
    
  • 2.IOC容器注冊及Action綁定授權

    //注冊服務
    services.AddSingleton<IAuthorizationHandler, MobilePhoneRequirement>();
    //注冊自定義授權策略
    services.AddAuthorization((AuthorizationOptions authorizationOptions) =>
    {
        authorizationOptions.AddPolicy("MobilePhone", policyBuilder => policyBuilder.Requirements.Add(new MobilePhoneRequirement()));
    });
    
     //控制器中使用
     [Authorize(AuthenticationSchemes = "Cookies", Policy = "MobilePhone")]
     public IActionResult InfoMobilePhone()
     {
         return View();
     }
    
2.授權析流程
1.授權注冊常用類
  • 1.如下幾個類

    類名 描述
    AuthorizationMiddleware 授權中間件
    IAuthorizationService >DefaultAuthorizationService 授權調用的服務
    IAuthorizationPolicyProvider > DefaultAuthorizationPolicyProvider 對於指定的請求,根據規則用於提供對應的Policy
    IAuthorizationHandlerProvider >DefaultAuthorizationHandlerProvider 提供最終對請求按照規則處理的Handler
    IPolicyEvaluator > PolicyEvaluator 用於驗證鑒權和授權為最終處理的AuthorizationService提供過渡
2.授權部分解析
  • 1.我們想知道最終的請求是如何被授權處理的,所以根據UseAuthorization(),找到授權中間件AuthorizationMiddleware,在中間件中實現了對請求的授權檢查。

    • 1.首先找到當前被調用目標是否IAuthorizeData標記的信息,其實就是查找被Authorize標記的特性,因為Authorize繼承自IAuthorizeData
    • 2.將IAuthorizeData 信息利用AuthorizationPolicy轉換為Policy.
    • 3.創建一個IPolicyEvaluator類型的PolicyEvaluator實例
    • 4.將ContextPolicy傳入PolicyEvaluator的AuthenticateAsync() 進行鑒權驗證返回一個AuthenticateResult
    • 5.判斷調用目標是否被AllowAnonymous標記,如果標記則直接跳過授權,進行訪問
    • 6.調用PolicyEvaluator的AuthorizeAsync()進行授權驗證,在方法中將處理轉交由IAuthorizationService處理,返回一個PolicyAuthorizationResult來說明是否授權成功。
  • 2.我們進入IAuthorizationService類型的實例DefaultAuthorizationService中查看

    • 1.根據規則,用戶,創建Context 上下文。
    • 2.根據上下文在DefaultAuthorizationHandlerProvider中獲取到繼承自IAuthorizationHandler對應的Requirement集合
    • 3.循環執行每一個實現IAuthorizationHandler的Requirement


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM