Security » Authorization » 基於自定義策略的授權


Custom Policy-Based Authorization 基於自定義策略的授權

98 of 108 people found this helpful

Underneath the covers the role authorization and claims authorization make use of a requirement, a handler for the requirement and a pre-configured policy. These building blocks allow you to express authorization evaluations in code, allowing for a richer, reusable, and easily testable authorization structure.

下面的內容覆蓋了role authorization(角色授權) 和claims authorization(聲明授權),確保了要求、要求的句柄以及一個預配制策略的使用,這些搭建好的模塊允許你在代碼中表達授權檢測,允許多重的、可重用的以及易於測試的授權結構。

An authorization policy is made up of one or more requirements and registered at application startup as part of the Authorization service configuration, in ConfigureServices in the Startup.cs file.

一個授權策略由一個或者多個要求組成,並且作為授權服務配置的一部分在應用啟動階段進行注冊(在Startup.cs文件的ConfigureServices )。

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

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

 

Here you can see an “Over21” policy is created with a single requirement, that of a minimum age, which is passed as a parameter to the requirement.

此處,你可看到新建的“Over21”策略使用了單項需求,表明了最小的年齡要求,並作為一個參數傳遞給需求。

Policies are applied using the Authorize attribute by specifying the policy name, for example;

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

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

    public ActionResult Logout()
    {
    }
}

 

Requirements 要求

An authorization requirement is a collection of data parameters that a policy can use to evaluate the current user principal. In our Minimum Age policy the requirement we have a single parameter, the minimum age. A requirement must implement IAuthorizationRequirement. This is an empty, marker interface. A parameterized minimum age requirement might be implemented as follows;

授權要求是一個數據參數組成的集合,這些參數可由策略使用來檢測當前用戶是否是授權用戶。在最小年齡策略要求中,有一個單個參數,表示最小年齡。一個要求必須繼承IAuthorizationRequirement。這是一個空的、起到標記作用的接口。參數化的最小年齡要求可以像下面這樣進行繼承。

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

    protected int MinimumAge { get; set; }
}

 

A requirement doesn’t need to have data or properties.

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

Authorization Handlers 授權處理器

An authorization handler is responsible for the evaluation of any properties of a requirement. The authorization handler must evaluate them against a provided AuthorizationContext to decide if authorization is allowed. A requirement can have multiple handlers. Handlers must inherit AuthorizationHandler<T> where T is the requirement it handles.

授權句處理器負責檢查一個要求的所有屬性。授權處理器必須按照提供的AuthorizationContext進行檢測, 以確定是否允許該項授權。一個要求可有多個處理器。處理器必須引用AuthorizationHandler<T>, 這里的T是所需處理器的泛型。

The minimum age handler might look like this:

最小年齡處理器看起來是這樣的:

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;
    }
}

 

In the code above we first look to see if the current user principal has a date of birth claim which has been issued by an Issuer we know and trust. If the claim is missing we can’t authorize so we return. If we have a claim, we figure out how old the user is, and if they meet the minimum age passed in by the requirement then authorization has been successful. Once authorization is successful we call context.Succeed() passing in the requirement that has been successful as a parameter.

在上面的代碼中,我們首先看一下,如果當前的用戶規則具有出生日期的聲明,該聲明由已知的並被信任的機構簽發。如果缺少這個聲明,我們不能進行授權,進而執行返回操作。如果有一個聲明,我們計算出用戶的年齡,並且如果年齡符合要求,這樣授權檢查就成功了。一旦授權成功,就在要求中調用context.Succeed(),並將成功驗證作為一個參數。

Handlers must be registered in the services collection during configuration, for example;

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

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

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

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

 

Each handler is added to the services collection by using services.AddSingleton<IAuthorizationHandler, YourHandlerClass>(); passing in your handler class.

每一個處理器都通過使用services.AddSingleton<IAuthorizationHandler, YourHandlerClass>()被添加進服務集合,傳遞到處理器類中。

What should a handler return? 一個處理器會返回什么

You can see in our handler example that the Handle() method has no return value, so how do we indicate success or failure?

可以看到在處理器的例子中,Handle()方法沒有返回值,所以我們如何表明成功或者失敗呢?

  • A handler indicates success by calling context.Succeed(IAuthorizationRequirement requirement), passing the requirement that has been successfully validated.
  • 一個處理器通過調用context.Succeed(IAuthorizationRequirement requirement)來表示成功,向要求傳遞已經進行了成功驗證的信息。
  • A handler does not need to handle failures generally, as other handlers for the same requirement may succeed.
  • 一般地,一個處理器不需要處理失敗信息,因為一個要求的其他處理就會滿足條件了。
  • To guarantee failure even if other handlers for a requirement succeed, call context.Fail.
  • 如果處理器的其他要求驗證成功了,仍要傳遞驗證失敗信息,可調用context.Fail

Regardless of what you call inside your handler all handlers for a requirement will be called when a policy requires the requirement. This allows requirements to have side effects, such as logging, which will always take place even if context.Fail() has been called in another handler.

無論您在您的處理程序中調用什么,當一個策略需要要求時,一個要求的所有處理程序都將會被調用。這使得要求有副作用,例如日志,盡管context.Fail()已在另一個處理程序被調用,它將總會發生。

Why would I want multiple handlers for a requirement?

為什么我需要一個復合處理器?

In cases where you want evaluation to be on an OR basis you implement multiple handlers for a single requirement. For example, Microsoft has doors which only open with key cards. If you leave your key card at home the receptionist prints a temporary sticker and opens the door for you. In this scenario you’d have a single requirement, EnterBuilding, but multiple handlers, each one examining a single requirement.

當需要檢測“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;
    }
}

 

Now, assuming both handlers are registered when a policy evaluates the EnterBuildingRequirement if either handler succeeds the policy evaluation will succeed.

現在,假定兩個處理器都被注冊了,當這個策略檢查EnterBuildingRequirement 時,如果每個處理器都會成功驗證。

Using a func to fufill a policy 使用函數滿足策略

There may be occasions where fufilling a policy is simple to express in code. It is possible to simply supply a Func<AuthorizationHandlerContext, bool> when configuring your policy with the RequireAssertion policy builder.

有這種場合,用代碼表達一個策略是簡單的。這個功能可以這樣來實現,當用RequireAssertion 策略構造器配置策略時,簡單地提供一個Func<AuthorizationHandlerContext, bool>

For example the previous BadgeEntryHandler could be rewritten as follows;

舉一個例子,前面的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"));
                          }));
    }
 }

 

Accessing MVC Request Context In Handlers 在處理器中使用MVC請求上下文

The Handle method you must implement in an authorization handler has two parameters, an AuthorizationContext and the Requirement you are handling. Frameworks such as MVC or Jabbr are free to add any object to the Resource property on the AuthorizationContext to pass through extra information.

在授權處理器中必須引用的Handle方法有兩個參數,一個是AuthorizationContext ,另一個是你正在處理的Requirement 。諸如MVC或者Jabbr等框架是開放的,可以向AuthorizationContextResource屬性添加任何對象,以傳遞特別的信息。

For example MVC passes an instance of Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext in the resource property which is used to access HttpContext, RouteData and everything else MVC provides.

例如,MVC向resource屬性傳遞了Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext 的一個實例,該實例用於連接HttpContext、RouteData以及MVC提供的其他一切。

The use of the Resource property is framework specific. Using information in the Resource property will limit your authorization policies to particular frameworks. You should cast the Resource property using the as keyword, and then check the cast has succeed to ensure your code doesn’t crash with InvalidCastExceptions when run on other frameworks;

使用Resource 屬性是由框架指定的。使用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