分享方式:


受保護 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");
        // ...
    }

不過您也可以使用控制器上的 [Authorize(Roles = "access_as_application")] 屬性或動作 (或 Razor 頁面)。

[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) 為基礎的授權模式來控制沒有 roles 宣告的權杖,藉此保護您的 Web API。

如果您在 ASP.NET Core 上使用 Microsoft.Identity.Web,則必須宣告您使用以 ACL 為基礎的授權,否則在提供的宣告中沒有角色或範圍時,Microsoft Identity Web 將會擲回例外狀況:

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

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

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

如果您將 AllowWebApiToBeAuthorizedByACL 設為 true,您必須負責確保 ACL 機制。

下一步

  • 若要深入了解,請在下列多部分教學課程系列中建置可登入使用者的 ASP.NET Core Web 應用程式

  • 探索 Microsoft 身分識別平台 Web API 範例