受保護的 Web API:驗證範圍和應用程式角色

本文說明如何將授權新增至 Web API。 此保護可確保只有下列方式呼叫 API:

  • 代表具有適當範圍和角色的用戶的應用程式。
  • 具有正確應用程式角色的精靈應用程式。

本文中的代碼段會從 GitHub 上的下列程式代碼範例擷取:

若要保護 ASP.NET 或 ASP.NET Core Web API,您必須將 屬性新增 [Authorize] 至下列其中一個專案:

  • 如果您想要保護所有控制器動作,控制器本身
  • API 的個別控制器動作
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

但這項保護還不夠。 它只保證 ASP.NET 和 ASP.NET Core 驗證令牌。 您的 API 必須確認用來呼叫 API 的令牌是使用預期的宣告要求。 這些宣告特別需要驗證:

  • 如果 API 是代表使用者呼叫,則 為範圍
  • 如果可以從精靈應用程式呼叫 API,則 為應用程式角色

代表使用者驗證 API 中的範圍

如果用戶端應用程式代表使用者呼叫您的 API,API 必須要求具有 API 特定範圍的持有人令牌。 如需詳細資訊,請參閱 程式代碼設定 |持有人令牌

在 ASP.NET Core 中,您可以使用 Microsoft.Identity.Web 來驗證每個控制器動作中的範圍。 您也可以在控制器層級或整個應用程式進行驗證。

確認每個控制器動作的範圍

您可以使用 屬性來驗證控制器動作 [RequiredScope] 中的範圍。 此屬性有數個覆寫。 直接採用必要範圍的一個,另一個採用組態索引鍵。

使用硬式編碼範圍確認控制器動作上的範圍

下列代碼段顯示屬性搭配硬式編碼範圍的使用 [RequiredScope] 方式。

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
使用組態中定義的範圍,確認控制器動作上的範圍

您也可以在設定中宣告這些必要的範圍,並參考組態索引鍵:

例如,在 appsettings.json 您有下列組態:

{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

然後,在屬性中 [RequiredScope] 參考它:

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
有條件地驗證範圍

在某些情況下,您想要有條件地驗證範圍。 您可以使用 上的HttpContext擴充方法來執行這項操作VerifyUserHasAnyAcceptedScope

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

確認控制器層級的範圍

您也可以驗證整個控制器的範圍

使用硬式編碼範圍確認控制器上的範圍

下列代碼段顯示屬性在 [RequiredScope] 控制器上使用硬式編碼範圍。 若要使用 RequiredScopeAttribute,您必須:

  • Startup.cs 中使用AddMicrosoftIdentityWebApi,如程式代碼設定所示
  • 否則,請將 新增ScopeAuthorizationRequirement至授權原則,如授權原則中所述
using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
使用組態中定義的範圍確認控制器上的範圍

如同動作,您也可以在設定中宣告這些必要的範圍,並參考組態索引鍵:

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

更全域地驗證範圍

定義 Web API 的細微範圍,並確認每個控制器動作中的範圍是建議的方法。 不過,您也可以驗證應用程式或控制器層級的範圍。 如需詳細資訊,請參閱 ASP.NET Core 檔中的宣告型授權

驗證的內容為何?

[RequiredScope]屬性和VerifyUserHasAnyAcceptedScope方法會執行如下的步驟:

  • 確認有名為 http://schemas.microsoft.com/identity/claims/scopescp的宣告。
  • 確認宣告具有值,其中包含 API 預期的範圍。

確認精靈應用程式所呼叫 API 中的應用程式角色

如果您的 Web API 是由 精靈應用程式呼叫,該應用程式應該需要 Web API 的應用程式許可權。 如公開應用程式許可權(應用程式角色)所示,您的 API 會公開這類許可權。 其中一個範例是 access_as_application 應用程式角色。

您現在必須讓 API 確認它收到的令牌包含 roles 宣告,且此宣告具有預期的值。 驗證碼類似於驗證委派許可權的程式代碼,不同之處在於您的控制器動作會測試角色,而不是範圍:

下列代碼段示範如何驗證應用程式角色;

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

相反地,您可以在控制器或動作(或 razor 頁面)上使用 [Authorize(Roles = "access_as_application")] 屬性。

[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

ASP.NET Core 中的角色型授權列出數種方法來實作角色型授權。 開發人員可以選擇其中一個適合其個別案例的案例。

如需工作範例,請參閱角色和群組授權的 Web 應用程式累加教學課程。

代表使用者驗證 API 中的應用程式角色

使用者也可以在使用者指派模式中使用角色宣告,如 如何在應用程式中新增應用程式角色,並在令牌中接收它們所示。 如果角色可指派給兩者,檢查角色會讓應用程式以使用者身分登入,而使用者以應用程式身分登入。 建議您為使用者和應用程式宣告不同的角色,以避免發生這種混淆。

如果您已使用使用者/群組定義應用程式角色,則角色宣告也可以在 API 中連同範圍一起驗證。 此案例中應用程式角色的驗證邏輯與精靈應用程式呼叫 API 相同,因為使用者/群組和應用程式的角色宣告沒有差異。

如果 Web API 只能由精靈應用程式呼叫,則接受僅限應用程式令牌

如果您只想讓精靈應用程式呼叫 Web API,請在驗證應用程式角色時新增令牌為僅限應用程式令牌的條件。

string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

檢查反向條件只允許登入使用者的應用程式呼叫您的 API。

使用 ACL 型授權

或者,除了以應用程式角色為基礎的授權,您也可以使用 存取控制 清單 (ACL) 型授權模式來保護 Web API,以控制令牌,而不需要roles宣告

如果您在 Microsoft.Identity.Web ASP.NET Core 上使用 ,則必須宣告您使用 ACL 型授權,否則當角色和範圍都未在提供的宣告中時,Microsoft Identity Web 會擲回例外狀況:

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

若要避免這個例外狀況,請在 AllowWebApiToBeAuthorizedByACL appsettings.json 或以程式設計方式將 組態屬性設定為 true

{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

如果您將 設定 AllowWebApiToBeAuthorizedByACLtrue,這是 您確保 ACL 機制的責任

下一步