Asp.Net Core--自定義基於策略的授權


翻譯如下:

  在封面下,角色授權和聲明授權使用需求,需求的處理程序和預配置的策略。 這些構建塊允許您在代碼中表示授權評估,從而允許更豐富,可重用和容易測試的授權結構。

  授權策略由一個或多個需求組成,並在應用程序啟動時作為授權服務配置的一部分注冊,在Startup.cs文件中的ConfigureServices中。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Over21",
                          policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
    }
});

  在這里您可以看到一個“Over21”策略創建有一個要求,最小年齡,作為參數傳遞到需求。

  通過指定策略名稱,使用Authorize屬性應用策略,例如:

[Authorize(Policy="Over21")]
public class AlcoholPurchaseRequirementsController : Controller
{
    public ActionResult Login()
    {
    }

    public ActionResult Logout()
    {
    }
}

  

Requirements(條件/要求)

  授權requirement是策略可用於評估當前用戶主體的數據參數的集合。 在我們的最低年齡政策中,要求我們有一個單一參數,即最低年齡。 要求必須實現IAuthorizationRequirement。 這是一個空的,標記接口。 參數化的最低年齡要求可以如下實現:  

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int age)
    {
        MinimumAge = age;
    }

    protected int MinimumAge { get; set; }
}

  一個requirement不需要具有數據或屬性。

 

授權處理程序

  授權處理程序負責評估requirement的任何屬性。 授權處理程序必須根據提供的AuthorizationContext來評估它們,以決定是否允許授權。 一個requirement可以有多個處理器。 處理程序必須繼承AuthorizationHandler <T>,其中T是它處理的requirement。

  最小年齡處理程序可能如下所示:

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationContext context, MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                   c.Issuer == "http://contoso.com"))
        {
            // .NET 4.x -> return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(
            c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

  在上面的代碼中,我們首先查看當前用戶主體是否具有由我們知道和信任的發行方發布的出生日期索賠日期。 如果索賠失蹤,我們不能授權,所以我們返回。 如果我們有一個索賠,我們計算出用戶的年齡,如果他們滿足最低年齡通過的要求,那么授權已經成功。 一旦授權成功,我們調用context.Succeed()在作為參數成功的需求中傳遞。

  處理程序必須在配置期間在服務集合中注冊,例如:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Over21",
                          policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

  每個處理程序通過使用services.AddSingleton <AuthorizationHandler,YourHandlerClass>();添加到服務集合。 傳入你的處理程序類。

 

處理程序需要返回什么?

  你可以在我們的處理程序示例中看到Handle()方法沒有返回值,那么我們如何指示成功或失敗? 

  處理程序通過調用context.Succeed(授權需求要求)來指示成功,傳遞已成功驗證的需求。

  處理程序不需要一般處理故障,因為相同需求的其他處理程序可能會成功。

  為了保證失敗,即使其他處理程序為一個需求成功,調用context.Fail。

  無論你在處理程序中調用什么,當策略需要該需求時,將調用一個需求的所有處理程序。 這允許需求具有副作用,例如日志記錄,即使在另一個處理程序中調用了context.Fail(),它也總是會發生。

 

為什么需要多個處理程序?

  如果您希望評估基於OR基礎,則為單個需求實現多個處理程序。 例如,微軟的門只能用鑰匙卡打開。 如果你把鑰匙卡留在家里,接待員打印一張臨時貼紙,為你打開門。 在這種情況下,您將有一個單獨的要求,EnterBuilding,但多個處理程序,每個檢查單個要求。

public class EnterBuildingRequirement : IAuthorizationRequirement
{
}

public class BadgeEntryHandler : AuthorizationHandler<EnterBuildingRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == ClaimTypes.BadgeId &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

public class HasTemporaryStickerHandler : AuthorizationHandler<EnterBuildingRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == ClaimTypes.TemporaryBadgeId &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

  現在,假設當策略評估輸入構建要求時注冊兩個處理程序,如果任一處理程序成功,則策略評估將成功。

 

使用Func來實現策略

  這里可以是其中完成策略在代碼中簡單表示的情況。 在使用RequireAssertion策略構建器配置策略時,可以簡單地提供Func <AuthorizationHandlerContext,bool>。

  例如,以前的BadgeEntryHandler可以被重寫如下:  

services.AddAuthorization(options =>
    {
        options.AddPolicy("BadgeEntry",
                          policy => policy.RequireAssertion(context =>
                                  context.User.HasClaim(c =>
                                     (c.Type == ClaimTypes.BadgeId ||
                                      c.Type == ClaimTypes.TemporaryBadgeId)
                                      && c.Issuer == "https://microsoftsecurity"));
                          }));
    }
 }

  

在處理程序中訪問MVC請求上下文

  您必須在授權處理程序中實現的Handle方法有兩個參數:AuthorizationContext和要處理的要求。 框架(如MVC或Jabbr)可以向AuthorizationContext的Resource屬性添加任何對象,以傳遞額外信息。

  例如,MVC在resource屬性中傳遞一個Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext的實例,該屬性用於訪問HttpContext,RouteData和MVC提供的所有內容。

  Resource屬性的使用是特定於框架的。 使用資源屬性中的信息將限制您的授權策略到特定框架。 您應該使用as關鍵字轉換Resource屬性,然后檢查轉換是否成功,以確保您的代碼在其他框架上運行時不會與InvalidCastExceptions崩潰;

var mvcContext = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;

if (mvcContext != null)
{
    // Examine MVC specific things like routing data.
}

  

 


免責聲明!

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



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