2017 年 10 月

第 32 卷,第 10 期

本文章是由機器翻譯。

最新 - ASP.NET Core 的原則式授權

Dino Esposito | 2017 年 10 月

Dino Esposito軟體應用程式的授權階層可確保目前的使用者可以存取指定的資源、 執行指定的作業或執行指定的資源上指定的作業。在 ASP.NET Core 有兩種方式可設定的授權層級。您可以使用角色,或者您可以使用原則。前一種方式,以角色為基礎的授權 — 已受到 ASP.NET Core 接觸以原則為基礎的授權時維護舊版 ASP.NET 平台。

Authorize 屬性

角色使用於自早期 ASP.NET 應用程式。就技術上來說,角色是純文字的字串。其值,不過,會視為 (IPrincipal 物件中的目前狀態的核取) 的安全性層級的中繼資訊和應用程式用來將一組權限對應到指定的已驗證使用者。在 ASP.NET 登入的使用者由 IPrincipal 物件,而在 ASP.NET Core 實際類別是宣告主體。這個類別會公開的身分識別的集合,每個身分識別物件,特別是宣告身分識別物件所表示。這表示任何登入的使用者隨附一份基本上她的狀態相關的陳述式的宣告。使用者名稱和角色是兩個常見的宣告的 ASP.NET Core 應用程式的使用者。不過,角色組態選項取決於備份身分識別存放區。比方說,如果您使用社交驗證時,您絕不要查看角色。

授權更進一步驗證。驗證是使用者的有關探索的身分識別,而是使用者的定義來呼叫應用程式端點的使用者需求的相關的授權。使用者角色通常儲存在資料庫中並擷取時進行驗證的使用者認證,在哪個點角色的使用者帳戶的一些方式附加資訊。IIdentity 介面的功能必須實作在角色中方法。宣告識別類別會檢查角色宣告為可用的驗證處理程序的宣告集合中。在任何情況下,當使用者嘗試呼叫安全控制站方法時,她角色應該要有的核取。如果沒有,使用者被拒絕任何受保護之方法的呼叫。

Authorize 屬性是維護安全的控制站或部分方法宣告式方法:

[Authorize]
public class CustomerController : Controller
{
  ...
}

指定不含引數,此屬性只會檢查會驗證使用者。不過,此屬性支援等角色的其他屬性。角色屬性會指出,在任何一個列出的角色會授與使用者存取。需要多個角色,您可以套用 Authorize 屬性許多次,或撰寫自己的篩選器。

[Authorize(Roles="admin, system"]
public class BackofficeController : Controller
{
  ...
}

(選擇性) Authorize 屬性也可以接受一或多個驗證配置,透過使用中的驗證配置屬性。

[Authorize(Roles="admin, system", ActiveAuthenticationSchemes="Cookie"]
public class BackofficeController : Controller
{
  ...
}

作用中的驗證配置屬性是以逗號分隔的字串,列出目前內容中將會信任的授權層級的驗證中介軟體元件。換句話說,指出使用者會透過 Cookie 配置進行驗證並有任何列出的角色時,才允許的存取權 Backoffice 控制器類別。如前所述,傳遞至使用中的驗證配置屬性的字串值必須符合在應用程式啟動時註冊的驗證中介軟體。

請注意,在 ASP.NET 2.0 中,驗證中介軟體會被取代的服務具有多個處理常式。如此一來,一種驗證配置會是選取的處理常式的標籤。如需 ASP.NET Core 中驗證的詳細資訊,您可能想要檢查 」 Cookie、 宣告和驗證在 ASP.NET Core"我 2017 年 9 月資料行 (msdn.com/magazine/mt842501)。

授權篩選條件

系統提供的授權篩選條件會耗用 Authorize 屬性所提供的資訊。它會負責檢查使用者是否能夠執行所要求的作業,因為此篩選條件會執行任何其他 ASP.NET Core 篩選之前,先。如果使用者未獲授權,篩選 short-circuits 管線,並取消要求。

您可以建立自訂的授權篩選條件,但大部分的情況下您不需要執行此作業。事實上,強烈建議您設定預設的篩選條件所依賴的現有授權階層。

角色、 權限,則會覆寫

角色是分組的功能可以或無法執行的動作為基礎的應用程式使用者的簡單方法。但它們不在表達;至少,不足以滿足大多數最新型應用程式的需求。例如,請考慮相當簡單的授權架構,一般使用者的網站和授權可存取後端辦公室軟體與更新內容的進階使用者提供服務。約兩個角色可以建立以角色為基礎的授權層 — 使用者和系統管理員: 定義每個群組的控制器和方法可以存取。

遇到問題就是描述哪些使用者可以或無法執行指定的角色中的細微差異則會覆寫。例如,您可能享受回 office 系統的存取權的使用者。使用者,有些已獲授權僅編輯客戶資料,有些是用於內容,但有些則兩者皆編輯客戶資料和工作的內容 (請參閱圖 1)。

角色的階層架構

圖 1 階層架構的角色

角色是基本上一般概念。您如何扁平化出甚至類似圖 1 所示的簡單階層?您可以建立四個不同角色-使用者、 系統管理員、 CustomerAdmin 和 ContentsAdmin — 但數目則會覆寫時,立即顯著增加的情況,必要的角色增加的數目。即使與下列類似的簡單練習將示範角色可能無法最有效的方式來處理授權,除了簡單的案例和回溯相容性所在的優先順序的執行個體。對於所有其他項目,沒有不同的需求。輸入原則為基礎的授權。

原則為何,?

在 ASP.NET Core 之以原則為基礎的授權架構被設計來分離授權和應用程式邏輯。簡單來說,原則是實體的本身也設計為需求的集合,是目前使用者必須符合的條件。

最簡單的原則是驗證使用者,而使用者是指定角色相關聯的常見的需求是。另一個常見的需求是,使用者有特定的宣告或特定值的特定宣告。在大多數的一般詞彙中,需求也是有關使用者身分嘗試存取的方法,為 true,則判斷提示。您建立的原則物件,使用下列程式碼:

var policy = new AuthorizationPolicyBuilder()
  .AddAuthenticationSchemes("Cookie, Bearer")
  .RequireAuthenticatedUser()
  .RequireRole("Admin")
  .RequireClaim("editor", "contents")  .RequireClaim("level", "senior")
  .Build();

產生器物件會收集使用各種不同的擴充方法的需求,並建立原則執行個體。如您所見,驗證狀態和配置、 角色和宣告的任何組合的處理需求是閱讀驗證 cookie 或持有人權杖。

如果沒有任何的預先定義的擴充方法定義的需求為您的工作,然後您可以一律求助於定義您自己的判斷提示透過新的需求。以下告訴您該如何做。

var policy = new AuthorizationPolicyBuilder()
  .AddAuthenticationSchemes("Cookie, Bearer")
  .RequireAuthenticatedUser()
  .RequireRole("Admin")
  .RequireAssertion(ctx =>
  {
    return ctx.User.HasClaim("editor", "contents") ||
           ctx.User.HasClaim("level", "senior");
  })
  .Build();

需要判斷提示方法會接收 Http 內容物件,並傳回布林值的 lambda。因此,判斷提示是只要條件陳述式。請注意,是否您多次串連需要的角色,然後所有角色必須都適用於使用者。如果您想要改為表示 OR 條件,就可能求助於判斷提示。在此範例中,事實上,此原則可讓編輯器的內容,或是初階使用者的使用者。

註冊原則

它是不夠的定義原則,您也必須註冊它們使用授權中介軟體。您可以將授權中介軟體新增為服務中設定的服務類別的方法啟動,就像這樣:

services.AddAuthorization(options=>
{
  options.AddPolicy("ContentsEditor", policy =>
  {
    policy.AddAuthenticationSchemes("Cookie, Bearer");
    policy.RequireAuthenticatedUser();
    policy.RequireRole("Admin");
    policy.RequireClaim("editor", "contents");
  });
}

新增中介軟體至每個原則都有名稱,用來參考在控制器類別上的授權屬性中的原則:

[Authorize(Policy = "ContentsEditor")]
public IActionResult Save(Article article)
{
  // ...
}

Authorize 屬性可讓您設定原則以宣告方式,但原則可以也叫用程式設計的方式直接從動作方法,如中所示圖 2

圖 2 以程式設計方式檢查原則

public class AdminController : Controller
{
  private IAuthorizationService _authorization;
  public AdminController(IAuthorizationService authorizationService)
  {
    _authorization = authorizationService;
  }

  public async Task<IActionResult> Save(Article article)
  {    var allowed = await _authorization.AuthorizeAsync(      User, "ContentsEditor"));
    if (!allowed)
      return new ForbiddenResult();
    
    // Proceed with the method implementation 
    ...
  }
}

如果權限以程式設計方式檢查失敗,您可能想要傳回禁止結果物件。另一個選項傳回挑戰的結果。在 ASP.NET Core 1.x,傳回一項挑戰是告訴授權中介軟體傳回 401 狀態碼,或將使用者重新導向至登入頁面上,視組態而定。重新導向不會發生在 ASP.NET Core 2.0 中,不過,而且即使在 ASP.NET Core 1.x 挑戰禁止的結果中如果使用者已經登入。最後,最好的方法會傳回禁止的結果,如果權限檢查失敗。

請注意,您甚至可以執行原則內 Razor 檢視,以程式設計方式檢查中的程式碼所示:

@{ 
  var authorized = await Authorization.AuthorizeAsync(
    User, "ContentsEditor"))}
@if (!authorized)
{
  <div class="alert alert-error">
    You’re not authorized to access this page.
  </div>
}

要使用此程式碼,不過,您必須先將插入的相依性授權服務,如下所示:

@inject IAuthorizationService Authorization

在檢視中使用授權服務,可協助隱藏不應該是 within the reach of 提供目前內容之目前使用者的使用者介面項目。請記住,不過,只要隱藏在檢視中的選項並不足夠。您一定要在控制站,以及強制執行原則。

自訂需求

內建需求涵蓋宣告,驗證並提供一般用途機制,做為自訂根據判斷提示,但是您可以建立自訂需求,太。原則需求由兩個項目: 的需求類別會保留是只有資料,且會驗證使用者對資料的授權處理常式。自訂需求擴充您 express 特定原則的能力。例如,假設您想要加入的需求,使用者必須擁有至少三年的經驗擴充內容編輯器原則。以下是您該怎麼做的:

public class ExperienceRequirement : IAuthorizationRequirement
{
  public int Years { get; private set; }

  public ExperienceRequirement(int minimumYears)
  {
    Years = minimumYears;
  }
}

要求必須至少一個授權的處理常式。處理常式是型別授權處理常式 < T >,其中 T 是需求類型。圖 3 說明經驗需求類型的範例處理常式。

圖 3 範例授權的處理常式

public class ExperienceHandler : 
             AuthorizationHandler<ExperienceRequirement>
{
  protected override Task HandleRequirementAsync( 
    AuthorizationHandlerContext context, 
    ExperienceRequirement requirement)
  {
    // Save User object to access claims
    var user = context.User;    if (!user.HasClaim(c => c.Type == "EditorSince"))      return Task.CompletedTask;

    var since = user.FindFirst("EditorSince").Value.ToInt();
    if (since >= requirement.Years)
      context.Succeed(requirement);

    return Task.CompletedTask;
  }
}

範例授權處理常式會讀取使用者相關聯的宣告,並檢查自訂編輯器因為宣告。如果找不到的此處理常式會傳回不成功。只有當宣告存在,且不小於指定的年數包含整數值,就會傳回成功。

自訂宣告必須是連結以某種方式給使用者的資訊 — 例如,使用者資料表的資料行 — 儲存在驗證 cookie。不過,一旦您保留使用者的參照,您可以一律尋找從宣告的使用者名稱並針對任何資料庫或外部服務的多年的經驗,並使用處理常式中的資訊執行查詢。(我承認本範例會是更逼真有編輯器因為值保留日期時間,以及計算使用者開始做為編輯器後,是否已經超過指定的年數。)

授權處理常式呼叫方法成功,將目前的需求,以通知已成功驗證需求。如果需求未通過,此處理常式不需要執行任何動作,並且可以只傳回。不過,如果此處理常式會想要判斷失敗的需求,不論在相同的需求上其他處理常式可能會成功的事實,它會呼叫方法失敗授權物件上。

以下是自訂的需求新增至原則 (保留,納入考量,因為這是自訂的需求,您的需要不到擴充方法,而是,您必須繼續執行需求的原則物件集合):

services.AddAuthorization(options =>
{
  options.AddPolicy("AtLeast3Years",
    policy => policy
      .Requirements
      .Add(new ExperienceRequirement(3)));
});

此外,您必須使用 IAuthorization 處理常式類型的範圍內的 DI 系統註冊新的處理常式:

services.AddSingleton<IAuthorizationHandler, ExperienceHandler>();

如前所述,一項需求可以有多個處理常式。當多個處理常式會向授權階層的相同需求的 DI 系統時,這樣便已足夠,至少一個成功。

存取目前的 HTTP 內容

在授權的處理常式的實作中,您可能需要檢查要求的屬性,或路由傳送資料,就像這樣:

if (context.Resource is AuthorizationFilterContext mvc)
{
  var url = mvc.HttpContext.Request.GetDisplayUrl();  ...
}

在 ASP.NET Core 授權處理常式內容物件會公開資源屬性設定為篩選條件內容物件。內容物件是涉及 framework 而有所不同。例如,MVC 和 SignalR 傳送他們自己的特定物件。是否轉換取決於您需要存取。例如,使用者資訊是一律,因此您不需要轉換,但是如果您想 MVC 特定詳細資料,例如路由資訊,然後您必須轉換。

總結

在 ASP.NET Core 授權有兩種類別。其中一個是傳統角色為基礎的授權,其運作方式的相同方式,它會在傳統的 ASP.NET MVC 中,並仍有正在平面並不適合用於表示複雜的授權邏輯的結構化限制。新的方法,提供更豐富且更具表達能力以外模型以原則為基礎的驗證。這是因為原則已根據宣告和任何其他資訊可插入從外部來源的 HTTP 內容為基礎的自訂邏輯的需求的集合。每個關聯具有一個或多個負責實際評估之需求的處理常式的這些需求。


Dino Esposito是作者"MICROSOFT.NET:架構的企業應用程式 」 (Microsoft Press,2014年) 和 「 使用 ASP.NET 的現代化 Web 應用程式 」 (Microsoft Press,2016年)。適用於.NET 和 JetBrains,在 Android 平台以及在產業活動全球技術推廣人員 Esposito 共用上的軟體的願景software2cents@wordpress.com和 Twitter 上: @despos

這點受惠檢閱本文章下列技術專家:Barry Dorrans (Microsoft) 和 Steve Smith


MSDN Magazine Magazine