2020/01/22更新---- 這難道不是.NET5 的bug? 在線求錘?


hello,最近在對一個使用.NET5項目的認證授權系統進行重構,對.NET 5的授權中間件的源碼有些看法。
也希望同學們能幫我理解。

一個朴素的需求

這是一個api項目,默認所有的api都需要授權, 少數散落在Controller各處的api不需要授權訪問,故這里有個全局授權訪問+特例匿名訪問的矛盾

以我粗鄙的想法,我相信.NET會很好的處理好這個矛盾: [AllowAnonymous]優先

這個想法在https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple?view=aspnetcore-5.0 得到印證。

需求實現

在Startup ConfigureServices添加認證、授權服務

  //  認證服務
 services.AddAuthentication("token")
          .AddScheme<TokenAuthenticationOptions, TokenAuthenticationHandler>(TokenAuthenticationDefaults.AuthenticationScheme,
          option => {
              option.ClaimsIssuer = configuration.GetSection("AppKeys")["ClaimsIssuer"].ToString();
              option.ClientId = configuration.GetSection("AppKeys")["ClientId"].ToString();
              option.ClientSign = configuration.GetSection("AppKeys")["ClientSign"].ToString();
    });

// 授權服務
services.AddAuthorization(options =>{
         // 默認策略
         options.DefaultPolicy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .AddAuthenticationSchemes("token")
                 .Build();
});

既然現在.NET5推薦使用端點路由的形式,故針對我這個朴素的需求:

我理所當然會嘗試使用在Controller端點上要求全局授權訪問,對散落在各地的不需要授權的Controller-Action添加[AllowAnonymous]特性

 // 注冊授權中間件
 app.UseAuthorization();
 app.UseEndpoints(endpoints =>
 {
     endpoints.MapHealthChecks("/healthz").AllowAnonymous().WithDisplayName("healthz");                  
     // 全局對所有api要求授權訪問
     endpoints.MapControllers().RequireAuthorization().WithDisplayName("default");
 });
[AllowAnonymous]
[HttpGet]
[Route("triggerorder")]
public void TriggerOrder()
{
  ...
}

實際測試發現,雖然我對Controller標記了允許匿名訪問, 但請求始終進入了授權認證過程!
這個·朴素的授權需求竟然還遇到了障礙。

探究源碼

授權中間件源碼在此: https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs

源碼很簡單:

1..NET 授權中間件先從端點獲取了全局授權聲明IAuthorizeData
2. 通過這個聲明拿到了詳細的全局授權策略
3. 后面直接開始走授權認證過程, ??? 難以理解
4. 雖然后面又開始檢測Controller-Action上面的AllowAnonymous特性,這時候已經晚了,你都把授權認證流程都走一遍了!!

很明顯,基於端點的全局授權+零散的匿名訪問特性 並沒有貫徹[AllowAnonymous]特性優先的原則

在這個測試例子中,當前端點的metadata確實包含AuthorizeAllowAnonymous兩個特性!

后續

我已經在github上提了issue(https://github.com/dotnet/aspnetcore/issues/29377), 講述了這個朴素的需求面臨的障礙,但是官方的回答我並不滿意。

暫時采用變通方案,我自行寫了一個授權中間件(主體拷貝自官方), 只是自行將對[AllowAnonymous]特性的檢測應用代碼提到端點授權代碼的前面, 這也是我內心認為的bug的修復方案。

歡迎大家留言,提出意見或看法!

---2020/01/22更新-------------------------------

之前我接收到各位大佬的留言: 不管是匿名還是鑒權訪問, 均需要登記在冊,再根據是MVC上否有匿名元數據(有的話,跳過),沒有就繼續走授權流程。

但是我又仔細檢視了源碼,發現並不完全正確, 請看官仔細觀察我上面的示例, 端點路由還有一個[健康檢查],端點上直接加上了[AllowAnonymous]

endpoints.MapHealthChecks("/healthz").AllowAnonymous().WithDisplayName("healthz");

這個端點並沒有進入認證流程,從授權中間件源碼上看也是如此。

故官方源碼是否能進入認證邏輯: 關鍵是看能不能在端點上直接找到授權策略

var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();   
            var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
            if (policy == null)
            {
                await _next(context);
                return;
            }

健康檢查端點直接應用了[AllowAnonymous](實際上你可以不加), 這樣就沒有授權策略(policy== null),這個時候自然跳過后續,進入業務邏輯。
甚至, 你可以這樣寫:

endpoints.MapControllers().RequireAuthorization().AllowAnonymous().WithDisplayName("default");

這樣的代碼也能進入認證邏輯,因為它包含了授權聲明.

根據以上分析,實際上端點上授權流程是這樣的:

以上給我們的結果是: 端點、MVC Controller上存在匿名訪問聲明時,其效果是不一樣的。

源碼對端點上的授權聲明(匿名.授權)的處理優先級 高於MVC Controller上的[AllowAnonymousAttribute].

或者說端點上的全局要求授權的處理優先級高於[AllowAnonymous],總之, 有特殊代碼在作怪


免責聲明!

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



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