Custom Policy-Based Authorization¶ 基於自定義策略的授權
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.
一個要求不需要有數據或屬性。
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等框架是開放的,可以向AuthorizationContext的Resource屬性添加任何對象,以傳遞特別的信息。
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. }