閱讀英文版本

分享方式:


在 ASP.NET Core 中設定 JWT 持有人驗證

達米安·鮑登

JWT (JSON Web 令牌) 持有人驗證通常用於 API。 雖然其運作方式與 cookie 驗證類似,但身分識別提供者會在驗證成功時發出 JWT 或令牌。 然後,這些令牌可以傳送到其他伺服器進行驗證,不同於只傳送回發行網域的 Cookie。 JWT 是獨立的令牌,可封裝 API 資源或客戶端的資訊。 要求 JWT 的用戶端可以使用授權標頭和持有人令牌,向 API 資源要求數據。

JWT 持有人認證提供:

  • 驗證:使用 JwtBearerHandler時,持有人令牌對於驗證至關重要。 JwtBearerHandler 會驗證令牌,並從其宣告中擷取使用者的身分識別。
  • 授權:持有人令牌藉由提供代表使用者或應用程式許可權的宣告集合來啟用授權,就像 cookie。
  • 委派的授權:當使用者特定的存取令牌用來在 API 之間進行驗證,而不是整個應用程式的存取令牌時,此程式稱為 委派的授權

如需了解 JWT 持有者身分驗證的簡介,請參閱 JSON Web 令牌。檢視或下載範例程式碼

本文涵蓋下列區域:

  • 令牌類型
  • 使用 JWT 令牌來保護 API
  • OIDC/OAuth 如何適用於這裡?
  • 實作 JWT 持有人令牌驗證
  • 建立 JWT 的建議方法

令牌類型

有許多類型的令牌和格式。 除了測試目的之外,不建議產生您自己的存取令牌或標識元令牌。 不符合已建立標準的自行創建代幣:

  • 可能會導致安全性弱點。
  • 僅適用於封閉式系統。

建議您使用 OpenID Connect 1.0 或 OAuth 標準來建立用於 API 存取的存取令牌。

存取令牌

存取令牌:

  • 用戶端應用程式會使用字串向實作 API 的伺服器提出要求。
  • 格式可能會有所不同。 不同的 API 可能會針對令牌使用不同的格式。
  • 可以加密。
  • 不應該由持有存取令牌的 Web 用戶端或 UI 應用程式讀取或解譯。
  • 僅供向 API 提出請求。
  • 通常會以持有人令牌的形式傳送至 授權 要求標頭中的 API。

請參閱 OAuth 2.0 授權架構

應用程式存取令牌和委派存取令牌

存取權杖可以是 應用程式存取權杖委派存取權杖。 令牌具有不同的屬性,並且會以不同的方式進行管理和儲存。 應用程式存取令牌 通常會儲存在應用程式中一次,直到到期為止,而 委派存取令牌 會儲存在每個使用者、cookie 或安全伺服器快取中。

每當涉及使用者時,我們建議使用委派的使用者存取令牌。 下游 API 可以代表已驗證的使用者要求委派的使用者存取令牌。

寄件者限制存取令牌

存取令牌可以作為 持有人令牌發送方限制令牌 存取資源。 受寄件者限制的令牌要求用戶端證明擁有私鑰才可使用令牌。 證明擁有私鑰可確保令牌無法獨立使用。 寄件者限制令牌可以透過兩種方式實現:

身份驗證憑證

標識元令牌是確認使用者成功驗證的安全性令牌。 令牌可讓客戶端驗證使用者的身分識別。 JWT 令牌伺服器會發出包含使用者資訊聲明的 ID 令牌。 ID 令牌一律採用 JWT 格式。

ID 令牌 絕對不應該 用來存取 API。

其他令牌

有許多類型的令牌,包括存取和標識符令牌,如 OpenID Connect 和 OAuth 標準所指定。 重新整理令牌可用來重新整理UI應用程式,而不需要重新驗證使用者。 OAuth JAR 令牌 可以安全地傳送授權要求。 可驗證的認證流程會利用 JWT 類型來發出或驗證認證。 請務必根據規格使用令牌。 如需詳細資訊,請參閱本文稍後提供的標準連結。

使用 JWT 令牌來保護 API

使用 JWT 存取令牌進行 API 授權時,API 會根據提供的令牌授與或拒絕存取。 如果要求未獲授權,則系統會傳回 401 或 403 回應。 API 不應該將使用者重新導向至識別提供者,以取得新的令牌或要求其他許可權。 取用 API 的應用程式負責取得適當的令牌。 這可確保在 API(授權)與取用的用戶端應用程式(驗證)之間清楚區分考慮。

備註

HTTP 也允許針對未授權傳回 404,以免將資源存在的相關信息洩漏給未經授權的用戶端。

401 未經授權

401 未經授權回應表示提供的存取令牌不符合所需的標準。 這可能是因為數個原因,包括:

  • 無效的簽名: 令牌的簽名不匹配,可能已被竄改。
  • 到期:令牌已過期且不再有效。
  • 不正確的聲明:令牌內的關鍵聲明,例如受眾(aud)或發行者(iss),存在遺失或無效的情況。

備註

從 HTTP 語意 RFC 9110:產生 401 回應的伺服器必須傳送 WWW-Authenticate 標頭字段 (第 11.6.1 節),其中包含至少一個適用於目標資源的挑戰。

OAuth 規格 提供關於必須宣告及其驗證的詳細指導方針。

403 禁止

403 禁止 回應通常表示已驗證的使用者缺少存取要求資源的必要許可權。 這與驗證問題不同,例如無效的令牌,而且與存取令牌中的標準宣告無關。

在 ASP.NET Core 中,您可以使用下列方式強制執行授權:

需求和原則:定義自定義需求,例如「必須是系統管理員」,並將其與原則產生關聯。 角色型授權:將使用者指派給角色,例如「系統管理員」、「編輯器」,並根據這些角色限制存取權。

在使用持有人令牌時,OIDC 和/或 OAuth 的角色是什麼?

當 API 使用 JWT 存取令牌進行授權時,API 只會驗證存取令牌,而不是令牌的取得方式。

OpenID Connect (OIDC) 和 OAuth 2.0 提供標準化、安全的令牌取得架構。 令牌擷取會根據應用程式類型而有所不同。 由於安全令牌擷取的複雜性,強烈建議您依賴下列標準:

  • 對於代表使用者和應用程式運作的應用程式:OIDC 是慣用的選擇,可啟用委派的使用者存取。 在 Web 應用程式中,建議使用適用於程式代碼 Exchange (PKCE) 證明金鑰的機密程式代碼流程,以提高安全性。
  • 如果應用程式沒有使用者:OAuth 2.0 用戶端認證流程適用於取得應用程式存取令牌。

實作 JWT 持有人令牌驗證

Microsoft.AspNetCore.Authentication.JwtBearer Nuget 套件可用來驗證 JWT 持有人令牌。

JWT 持有人令牌應該在 API 中完整驗證。 應該驗證下列項目:

  • 簽章,象徵信任與誠信。 這可確保令牌是由指定的安全令牌服務所建立,而且未遭到竄改。
  • 具有預期值的發行者聲明。
  • 具有預期值的受眾聲稱。
  • 令牌到期。

OAuth 2.0 存取令牌需要下列宣告:issexpaudsubclient_idiatjti

如果上述任何宣告或值不正確,API 應該會傳回 401 回應。

JWT 持有人令牌基本驗證

AddJwtBearer 的基本實作可以只驗證使用者和發行者。 簽章必須經過驗證,才能信任令牌,且尚未遭到竄改。

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtOptions =>
{
	jwtOptions.Authority = "https://{--your-authority--}";
	jwtOptions.Audience = "https://{--your-audience--}";
});

JWT 持有人令牌明確驗證

AddJwtBearer 方法提供多個組態。 某些安全令牌提供者會使用非標準元數據位址,而且可以明確設定 參數。 API 可以接受多個簽發者或受眾。

不需要明確定義參數。 定義取決於存取令牌宣告值,以及用來驗證存取令牌的安全令牌伺服器。 如果可能,您應該使用預設值。

請參閱對應宣告 的 mapInboundClaims 詳細資料。

builder.Services.AddAuthentication()
.AddJwtBearer("some-scheme", jwtOptions =>
{
	jwtOptions.MetadataAddress = builder.Configuration["Api:MetadataAddress"];
	// Optional if the MetadataAddress is specified
	jwtOptions.Authority = builder.Configuration["Api:Authority"];
	jwtOptions.Audience = builder.Configuration["Api:Audience"];
	jwtOptions.TokenValidationParameters = new TokenValidationParameters
	{
		ValidateIssuer = true,
		ValidateAudience = true,
		ValidateIssuerSigningKey = true,
		ValidAudiences = builder.Configuration.GetSection("Api:ValidAudiences").Get<string[]>(),
		ValidIssuers = builder.Configuration.GetSection("Api:ValidIssuers").Get<string[]>()
	};

	jwtOptions.MapInboundClaims = false;
});

具有多個方案的 JWT

API 通常需要容納來自各種簽發者的存取令牌。 您可以在 API 中支援多個權杖簽發者,方法是:

  • 個別 API:為每個簽發者建立具有專用驗證配置的不同 API。
  • AddPolicyScheme 此方法可以定義多個驗證方案,並執行邏輯,根據令牌屬性選取適當的方案(例如簽發者、聲明)。 這種方法可讓您在單一 API 內有更大的彈性。

強制持有人驗證

SetDefaultPolicy 可以用來要求對所有請求進行驗證,即使是沒有 [Authorize] 屬性的端點。 SetDefaultPolicy 設定用於具有 [Authorize] 屬性之端點的原則,並且已預設為要求已驗證的使用者。 如需詳細資訊,請參閱 已驗證用戶所需的文件

var requireAuthPolicy = new AuthorizationPolicyBuilder()
	.RequireAuthenticatedUser()
	.Build();

builder.Services.AddAuthorizationBuilder()
	.SetDefaultPolicy(requireAuthPolicy);

Authorize 屬性也可用來強制驗證。 如果使用多個方案,承載者機制通常必須設定為默認驗證機制,或透過 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme])指定。

控制器中的授權:

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{

輕量 API 中的授權:

app.MapGet("/hello", [Authorize] () => "Hi");

不安全的存取令牌處理,例如弱式驗證或將令牌儲存在易受攻擊的用戶端記憶體中,可能會導致嚴重的安全性弱點。 例如,直接在瀏覽器中使用本地存儲、會話存儲或網頁工作者來儲存存取令牌。 下一節包含使用和建立存取令牌之應用程式的最佳做法。

使用標準

OpenID Connect 或 OAuth 等標準應該 一律 建立存取令牌時使用。 存取令牌應該 不要 在生產應用程式中建立,而不需要遵守本文中所述的安全性預防措施。 建立存取令牌應該僅限於測試案例。

使用非對稱金鑰

非對稱金鑰應該 一律 建立存取令牌時使用。 公鑰可在已知的端點中使用,而 API 用戶端可以使用公鑰來驗證存取令牌的簽章。

永遠不要從使用者名稱/密碼請求中建立存取令牌

您應該 不應該 從使用者名稱/密碼要求建立存取令牌。 使用者名稱/密碼要求不會經過驗證,而且容易受到模擬和網路釣魚攻擊的影響。 只能使用 OpenID Connect 流程或 OAuth 標準流程來建立存取令牌。 偏離這些標準可能會導致不安全的應用程式。

使用 Cookie

針對安全的 Web 應用程式,需要後端才能將存取令牌儲存在受信任的伺服器上。 只有安全的 HTTP cookie 會在用戶端瀏覽器上共用。 如需如何在 ASP.NET Core Web 應用程式中執行這項操作,請參閱 OIDC 驗證檔

下游 API

API 偶爾需要代表呼叫應用程式中已驗證的使用者,從下游 API 存取用戶數據。 雖然實作 OAuth 用戶端認證流程是一個選項,但兩個 API 應用程式之間必須完全信任。 使用更安全的方法涉及零信任策略配合委派的使用者存取令牌。 此方法:

  • 為了增強安全性,將 API 授予該特定使用者所需的必要許可權。
  • 要求 API 為呼叫應用程式和 API 的使用者建立新的存取令牌。

有數種方式可以使用委派的使用者存取令牌來實作零信任策略:

使用 OAuth 2.0 令牌交換來要求新的委派存取令牌

這是實作此需求的好方法,但如果您必須實作 OAuth 流程,則很複雜。

請參閱 OAuth 2.0 Token Exchange

代表流程使用 Microsoft Identity Web 來要求新的委派存取令牌

使用 Microsoft Identity Web 驗證連結庫 是最簡單且安全的方法。 它只適用於 Microsoft Entra ID 和 Microsoft Entra External ID。

如需詳細資訊,請參閱 [Microsoft 身分識別平台和 OAuth 2.0 流程Behalf-Of

使用傳送至 API 的相同委派存取令牌

這種方法並不容易實作,但存取令牌可以存取所有下游 API。 Yarp 反向 Proxy 可用來實作此動作。

使用 OAuth 用戶端認證流程,並使用應用程式存取令牌

這很容易實作,但用戶端應用程式具有完整的應用程式存取權,而不是委派的存取令牌。 令牌應該在用戶端 API 應用程式中快取。

備註

任何應用程式之間的安全性也適用。 您可以使用憑證驗證,或在 Azure 中使用受控識別。

處理存取令牌

在用戶端應用程式中使用存取令牌時,必須在伺服器上輪替、保存及儲存存取令牌。 在 Web 應用程式中,cookies 可用來保護工作階段,並可透過 SaveTokens 選項來儲存令牌。

SaveTokens 目前不會自動重新整理存取令牌,但這項功能已規劃為 .NET 10。 請關注 https://github.com/dotnet/aspnetcore/issues/8175 以獲取更新。 同時,您可以按照 OIDC 文件 和 所示,手動刷新存取令牌 ,或使用第三方 NuGet 套件,例如 Duende.AccessTokenManagement.OpenIdConnect,來處理和管理客戶端應用中的存取令牌。 如需詳細資訊,請參閱 Duende 令牌管理

備註

如果部署到生產環境,快取應該在多重實例部署中能夠正常運作。 通常需要持續性快取。

某些安全令牌伺服器會加密存取令牌。 存取令牌不需要任何格式。 使用 OAuth 檢視時,會使用參考令牌,而不是存取令牌。 用戶端 (UI) 應用程式不應該開啟存取令牌,因為存取令牌不適用於此專案。 只有建立存取令牌的 API 才應該開啟存取令牌。

  • 請勿在 UI 應用程式中開啟存取令牌
  • 請勿將標識碼令牌傳送至 API
  • 存取令牌可以有任何格式
  • 存取令牌可以加密
  • 存取令牌到期且需要輪替
  • 存取令牌會保存在安全的後端伺服器上

YARP(另一個反向代理)

YARP(又一個反向 Proxy) 是處理 HTTP 要求並將要求轉送至其他 API 的好工具。 YARP 可以實作安全性邏輯來取得新的存取認證。 YARP 經常用於前端 (BFF) 安全性架構的 後端。 OIDC 文件 Blazor Web App 示範如何使用 YARP 來實作 BFF 模式。

測試 API

整合測試和具有存取令牌的容器可用來測試安全的API。 使用 dotnet user-jwts 工具即可建立存取令牌。

警告

請確定安全性問題 不會 導入 API 以供測試之用。 使用委派存取令牌時,測試會變得更具挑戰性,因為這些令牌只能透過UI和OpenID Connect流程建立。 如果使用測試工具來建立委派存取令牌,則必須停用安全性功能以進行測試。 在測試環境中,這些功能必須停用。

建立可安全地停用或修改安全性功能的專用和隔離測試環境。 請確定這些變更嚴格限於測試環境。

使用 Swagger UI、Curl 和其他 API UI 工具

Swagger UI 和 Curl 是測試 API 的絕佳 UI 工具。 若要讓工具運作,API 可以產生 OpenAPI 檔,這可以載入客戶端測試工具。 取得新存取令牌的安全性流程可以新增至 API OpenAPI 檔案。

警告

請勿將不安全的安全性測試流程部署到生產環境。

為 API 實作 Swagger UI 時,您通常不應該將 UI 部署到生產環境,因為安全性必須減弱,才能讓此運作。

對應 OpenID Connect 的宣告

請參閱下列檔案:

在 ASP.NET Core 中對應、自定義和轉換宣告

標準

JSON Web 令牌 (JWT)

OAuth 2.0 授權架構

OAuth 2.0 示範擁有權證明 DPoP

OAuth 2.0 JWT-Secured 授權要求(JAR)RFC 9101

OAuth 2.0 Mutual-TLS 客戶端驗證和 Certificate-Bound 存取令牌

OpenID Connect 1.0

Microsoft 身分識別平臺與 OAuth 2.0 的Behalf-Of 流程

OAuth 2.0 令牌交換

適用於 OAuth 2.0 存取令牌的 JSON Web 令牌 (JWT) 配置檔

HTTP 語意 RFC 9110


更多資源

文件