使用 Microsoft Entra 識別子保護 ASP.NET Core Blazor Web App
本文說明如何使用Blazor Web App範例應用程式,透過 Microsoft identity 平臺/Microsoft Identity Web 套件來保護 Microsoft Entra ID。
涵蓋下列規格:
- 使用Blazor Web App自動轉譯模式搭配全域互動性 (
InteractiveAuto
)。 - 伺服器專案會呼叫 AddAuthenticationStateSerialization ,以新增伺服器端驗證狀態提供者,以用來 PersistentComponentState 將驗證狀態流向用戶端。 用戶端會呼叫 AddAuthenticationStateDeserialization 來還原串行化,並使用伺服器所傳遞的驗證狀態。 該驗證狀態已針對 WebAssembly 應用程式的存留期修正。
- 應用程式會根據 Identity Microsoft Web 套件使用 Microsoft Entra 識別碼。
- 自動非互動式令牌重新整理是由架構所管理。
- 應用程式會使用伺服器端和客戶端服務抽象概念來顯示產生的天氣資料:
- 在伺服器上轉譯
Weather
元件以顯示天氣數據時,元件會使用ServerWeatherForecaster
伺服器上的 直接取得天氣數據(而非透過 Web API 呼叫)。 Weather
當元件在用戶端上轉譯時,元件會使用ClientWeatherForecaster
服務實作,該實作會使用預先設定HttpClient的 (在用戶端專案的Program
檔案中) 對伺服器專案的最小 API (/weather-forecast
) 進行 Web API 呼叫,以取得天氣數據。 最小 API 端點會從ServerWeatherForecaster
類別取得天氣數據,並將它傳回給用戶端,以供元件轉譯。
- 在伺服器上轉譯
範例應用程式
這個應用程式範例包含二個專案:
BlazorWebAppEntra
: Blazor Web App的伺服器端專案,包含天氣資料的 最小 API 端點範例。BlazorWebAppEntra.Client
: Blazor Web App的用戶端專案。
使用下列連結,透過存放庫根目錄中的最新版本資料夾存取範例應用程式。 項目位於 BlazorWebAppEntra
.NET 9 或更新版本的資料夾中。
檢視或下載範例程式碼 \(英文\) (如何下載)
伺服器端 Blazor Web App 專案 (BlazorWebAppEntra
)
BlazorWebAppEntra
專案是 Blazor Web App的伺服器端專案。
BlazorWebAppEntra.http
檔案可用於測試天氣資料要求。 請注意,BlazorWebAppEntra
專案必須執行才能測試端點,而且端點會硬式編碼到檔案中。 如需詳細資訊,請參閱在 Visual Studio 2022 中使用 .http 檔案。
Blazor Web App 用戶端專案 (BlazorWebAppEntra.Client
)
BlazorWebAppEntra.Client
專案是 Blazor Web App的用戶端專案。
如果使用者需要在用戶端轉譯期間登入或註銷,則會起始完整頁面重載。
組態
本節說明如何設定應用程式範例。
AddMicrosoftIdentityWebApp來自 Microsoft Identity Web (Microsoft.Identity.Web
NuGet 套件,API 檔) 是由AzureAd
伺服器專案檔案的 appsettings.json
區段所設定。
在 Entra 或 Azure 入口網站 的應用程式註冊中,使用具有 重新導向 URI https://localhost/signin-oidc
的 Web 平台組態(不需要埠)。 確認未選取 [隱含授與] 和 [混合式流程] 底下的標識碼令牌和存取令牌。 OpenID Connect 處理程式會使用從授權端點傳回的程式代碼,自動要求適當的令牌。
設定應用程式
在伺服器專案的應用程式設定檔中,appsettings.json
提供應用程式的 AzureAd
區段組態。 從 Entra 或 Azure 入口網站 中的應用程式註冊取得應用程式 (client) 識別碼、租使用者 (publisher) 網域和目錄 (tenant) 識別碼:
"AzureAd": {
"CallbackPath": "/signin-oidc",
"ClientId": "{CLIENT ID}",
"Domain": "{DOMAIN}",
"Instance": "https://login.microsoftonline.com/",
"ResponseType": "code",
"TenantId": "{TENANT ID}"
},
上述範例中的佔位元:
{CLIENT ID}
:應用程式 (用戶端) 識別碼。{DOMAIN}
:租使用者 (publisher) 網域。{TENANT ID}
:目錄(租用戶)標識符。
範例:
"AzureAd": {
"CallbackPath": "/signin-oidc",
"ClientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
"Domain": "contoso.onmicrosoft.com",
"Instance": "https://login.microsoftonline.com/",
"ResponseType": "code",
"TenantId": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
},
回呼路徑 (CallbackPath
) 必須符合在 Entra 或 Azure 入口網站 中註冊應用程式時所設定的重新導向 URI(登入回呼路徑)。 路徑是在 應用程式註冊的 [驗證 ] 刀鋒視窗中設定。 的預設值 CallbackPath
為 /signin-oidc
的已註冊重新導向 URI https://localhost/signin-oidc
(不需要埠)。
警告
請勿在用戶端程式代碼中儲存應用程式密碼、連接字串、認證、密碼、個人標識元(PIN)、私人 C#/.NET 程式代碼或私鑰/令牌,這一律不安全。 在測試/預備和生產環境中,伺服器端 Blazor 程序代碼和 Web API 應該使用安全驗證流程,以避免在專案程式代碼或組態檔內維護認證。 在本機開發測試之外,建議您避免使用環境變數來儲存敏感數據,因為環境變數不是最安全的方法。 針對本機開發測試, 建議使用秘密管理員工具 來保護敏感數據。 如需詳細資訊,請參閱 安全地維護敏感數據和認證。
建立客戶端密碼
在 Entra 或 Azure 入口網站 中,於應用程式的 Entra 識別元註冊中建立用戶端密碼(管理>憑證和秘密>新用戶端密碼)。 在下列指引中使用新秘密的值。
使用下列任一或兩種方法,將客戶端密碼提供給應用程式:
- 秘密管理員工具:秘密管理員工具會將私人數據儲存在本機計算機上,而且只會在本機開發期間使用。
- Azure 金鑰保存庫:您可以將用戶端密碼儲存在密鑰保存庫中,以用於任何環境,包括在本機工作時用於開發環境。 有些開發人員偏好使用金鑰保存庫進行預備和生產部署,並使用 秘密管理員工具來 進行本機開發。
強烈建議您避免將客戶端密碼儲存在專案程式代碼或組態檔中。 使用安全驗證流程,例如本節中任一或兩種方法。
秘密管理員工具
秘密 管理員工具 可以在組態金鑰 AzureAd:ClientSecret
下儲存伺服器應用程式的客戶端密碼。
尚未 初始化秘密管理員工具的範例應用程式 。 使用命令殼層,例如 Visual Studio 中的開發人員 PowerShell 命令殼層,執行下列命令。 在執行命令之前,請將 目錄與 cd
命令變更為伺服器項目的目錄。 命令會在伺服器應用程式的項目檔中建立使用者秘密識別碼 (<UserSecretsId>
),此工具會在內部使用此工具來追蹤應用程式的秘密:
dotnet user-secrets init
執行下列命令來設定客戶端密碼。 佔 {SECRET}
位元是從應用程式的 Entra 註冊取得的客戶端密碼:
dotnet user-secrets set "AzureAd:ClientSecret" "{SECRET}"
如果使用 Visual Studio,您可以在 方案總管 中以滑鼠右鍵按兩下伺服器專案,然後選取 [管理用戶密碼],以確認密碼已設定。
Azure Key Vault
Azure 金鑰保存庫 提供安全的方法來提供應用程式的用戶端密碼給應用程式。
若要建立密鑰保存庫並設定用戶端密碼,請參閱關於 Azure 金鑰保存庫 秘密 (Azure 檔),其中會跨鏈接資源以開始使用 Azure 金鑰保存庫。 若要在本節中實作程序代碼,請在建立密鑰保存庫和秘密時,記錄來自 Azure 的密鑰保存庫 URI 和秘密名稱。 當您在 [存取 原則] 面板中設定秘密的存取原則時:
- 只需要取得秘密許可權。
- 選取應用程式作為 秘密的主體 。
重要
金鑰保存庫密碼會以到期日建立。 請務必追蹤金鑰保存庫秘密何時到期,並在該日期通過之前為應用程式建立新的秘密。
下列 GetKeyVaultSecret
方法會從金鑰保存庫擷取秘密。 將此方法新增至伺服器專案。 調整命名空間 (BlazorSample.Helpers
) 以符合您的專案命名空間配置。
Helpers/AzureHelper.cs
:
using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
namespace BlazorSample.Helpers;
public static class AzureHelper
{
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
{
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = tenantId,
SharedTokenCacheTenantId = tenantId
};
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
var secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
在伺服器專案檔案 Program
中註冊服務的位置,請使用下列程式代碼取得並套用客戶端密碼:
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
var secretName = builder.Configuration.GetValue<string>("AzureAd:SecretName")!;
builder.Services.Configure<MicrosoftIdentityOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.ClientSecret =
AzureHelper.GetKeyVaultSecret(tenantId, vaultUri, secretName);
});
如果您想要控制上述程式代碼運作的環境,例如避免在本機執行程式碼,因為您選擇使用 秘密管理員工具 進行本機開發,您可以將上述程式碼包裝在檢查環境的條件語句中:
if (!context.HostingEnvironment.IsDevelopment())
{
...
}
在 的 appsettings.json
區AzureAd
段中,新增下列 VaultUri
和 SecretName
組態索引鍵和值:
"VaultUri": "{VAULT URI}",
"SecretName": "{SECRET NAME}"
在前述範例中:
- 佔
{VAULT URI}
位元是金鑰保存庫 URI。 在 URI 中包含尾端斜線。 - 佔
{SECRET NAME}
位元是秘密名稱。
範例:
"VaultUri": "https://contoso.vault.azure.net/",
"SecretName": "BlazorWebAppEntra"
組態可用來根據應用程式的環境組態檔,提供專用的金鑰保存庫和秘密名稱。 例如,您可以在開發中、appsettings.Staging.json
預備階段和appsettings.Production.json
生產部署時提供不同的組態值appsettings.Development.json
。 如需詳細資訊,請參閱 ASP.NET Core Blazor 組態。
重新導向至 home 註銷頁面
使用者瀏覽應用程式時,LogInOrOut
元件 (Layout/LogInOrOut.razor
) 會將傳回 URL (ReturnUrl
) 的隱藏欄位設定為目前 URL (currentURL
) 的值。 使用者登出應用程式時, identity 提供者會讓他們返回登出時的頁面。
如果使用者從安全頁面登出,則只有在登出後,才會透過驗證流程返回相同的安全頁面。 使用者需要經常切換帳戶時,這個行為完全沒問題。 不過,替代的應用程式規格可能會要求使用者在註銷后,傳回應用程式 home 頁面或其他頁面。下列範例示範如何將應用程式的 home 頁面設定為註銷作業的傳回 URL。
下列範例示範 LogInOrOut
元件的重要變更。 不需要提供設為頁面/
的home隱藏字段ReturnUrl
,因為這是預設路徑。 已不再實作 IDisposable。 已不再插入 NavigationManager。 已移除整個 @code
區塊。
Layout/LogInOrOut.razor
:
@using Microsoft.AspNetCore.Authorization
<div class="nav-item px-3">
<AuthorizeView>
<Authorized>
<form action="authentication/logout" method="post">
<AntiforgeryToken />
<button type="submit" class="nav-link">
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
</span> Logout @context.User.Identity?.Name
</button>
</form>
</Authorized>
<NotAuthorized>
<a class="nav-link" href="authentication/login">
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span>
Login
</a>
</NotAuthorized>
</AuthorizeView>
</div>
疑難排解
記錄
伺服器應用程式是標準 ASP.NET Core 應用程式。 請參閱 ASP.NET Core 記錄指導,在伺服器應用程式啟用較低的記錄層級。
若要啟用 Blazor WebAssembly 驗證的偵錯或追蹤記錄,請參閱 ASP.NET Core Blazor 記錄的用戶端驗證記錄一節,並將發行項版本選取器設定為 ASP.NET Core 7.0 或更新版本。
常見錯誤
應用程式或 Identity 提供者 (IP) 的設定錯誤
最常見的錯誤是由不正確的設定所造成。 以下是一些範例:
- 視案例的需求而定,遺漏或不正確的授權單位、執行個體、租用戶識別碼、租用戶網域、用戶端識別碼或重新導向 URI 會防止應用程式驗證用戶端。
- 不正確的要求範圍會防止用戶端存取伺服器 Web API 端點。
- 不正確或遺漏伺服器 API 權限會防止用戶端存取伺服器 Web API 端點。
- 在與 IP 應用程式註冊的重新導 URI 中設定的連接埠不同的連接埠上執行應用程式。 請注意,Microsoft Entra ID 和在
localhost
開發測試位址執行的應用程式不需要連接埠,但應用程式的連接埠設定和執行應用程式的連接埠必須與非localhost
位址相符。
本文涵蓋的設定,顯示正確設定的範例。 請仔細查看設定,確定應用程式和 IP 是否設定錯誤。
如果設定顯示正確:
分析應用程式記錄檔。
使用瀏覽器的開發人員工具,檢查用戶端應用程式與 IP 或伺服器應用程式之間的網路流量。 通常,在提出要求之後,IP 或伺服器應用程式會傳回錯誤訊息或有導致問題的線索訊息給用戶端。 下列文章中可找到開發人員工具指導:
- Google Chrome (Google 文件)
- Microsoft Edge
- Mozilla Firefox (Mozilla 文件)
文件小組會回應文章中的文件意見反應和 BUG (從此頁面意見反應區段開啟問題),但是無法提供產品支援。 有數個公用支援論壇可用來協助針對應用程式進行疑難排解。 我們建議下列事項:
上述論壇並非由 Microsoft 擁有或控制。
針對非安全性、非敏感性和非機密可重現架構 BUG 報告,向 ASP.NET Core 產品單位提出問題。 在您徹底調查問題的原因而且無法自行解決,並取得公用支援論壇上社群的協助之前,請勿向產品單位提出問題。 產品單位無法針對因簡單設定錯誤或涉及第三方服務的使用案例而中斷的個別應用程式進行疑難排解。 如果報告具有敏感性或機密性質,或描述了攻擊者可能惡意探索的產品中潛在的安全性缺陷,請參閱 報告安全性問題和 BUG (
dotnet/aspnetcore
GitHub 存放庫)。ME-ID 未經授權的用戶端
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. 不符合以下需求: DenyAnonymousAuthorizationRequirement:需要已驗證的使用者。
ME-ID 的登入回呼錯誤:
- 錯誤:
unauthorized_client
- 描述:
AADB2C90058: The provided application is not configured to allow public clients.
若要解決此錯誤:
- 在 Azure 入口網站中,存取應用程式的資訊清單。
- 將
allowPublicClient
屬性設定為null
或true
。
- 錯誤:
Cookie 和網站資料
Cookie 和網站資料可以在應用程式更新之間保存,並可介入測試和疑難排解。 進行應用程式程式碼變更、使用提供者的使用者帳戶變更,或提供者應用程式設定變更時,請清除下列內容:
- 使用者登入 Cookie
- 應用程式 Cookie
- 快取和儲存的網站資料
防止徘徊的 cookie 和網站資料不會因使用測試和疑難排解而介入的一種方法是:
- 設定瀏覽器
- 使用瀏覽器進行測試,您可以設定在每次關閉瀏覽器時刪除所有 cookie 和網站資料。
- 請確定瀏覽器已手動關閉或由 IDE 關閉,以便對應用程式、測試使用者或提供者設定進行任何變更。
- 使用自訂命令,在 Visual Studio 中以私人模式或無痕模式開啟瀏覽器:
- 從 Visual Studio 的 [執行] 按鈕開啟 [瀏覽方式] 對話方塊。
- 選取新增按鈕。
- 在 [程式] 欄位中提供瀏覽器的路徑。 下列可執行檔路徑是 Windows 10 的一般安裝位置。 如果您的瀏覽器安裝在不同的位置,或您不是使用 Windows 10,請提供瀏覽器可執行檔的路徑。
- Microsoft Edge:
C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
- Google Chrome:
C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
- Mozilla Firefox:
C:\Program Files\Mozilla Firefox\firefox.exe
- Microsoft Edge:
- 在 [引數] 欄位中,提供瀏覽器用來在私人模式或無痕模式中開啟的命令列選項。 某些瀏覽器需要應用程式的 URL。
- Microsoft Edge:使用
-inprivate
。 - Google Chrome:使用
--incognito --new-window {URL}
,其中{URL}
預留位置是要開啟的 URL (例如https://localhost:5001
)。 - Mozilla Firefox:使用
-private -url {URL}
,其中{URL}
預留位置是要開啟的 URL (例如https://localhost:5001
)。
- Microsoft Edge:使用
- 在 [自訂名稱] 欄位中提供名稱。 例如:
Firefox Auth Testing
。 - 選取確定按鈕。
- 若要避免針對使用應用程式測試的每個反覆項目選取瀏覽器設定檔,請使用 [設為預設值] 按鈕,將設定檔設定為預設值。
- 請確定瀏覽器已由 IDE 關閉,以便對應用程式、測試使用者或提供者設定進行任何變更。
應用程式升級
在升級開發電腦上的 .NET Core SDK 或變更應用程式內的套件版本之後,正常運作的應用程式便立即發生失敗。 在某些情況下,執行主要升級時,不一致的套件可能會中斷應用程式。 大多數這些問題都可依照下列指示來進行修正:
- 從命令殼層執行
dotnet nuget locals all --clear
,以清除本機系統的 NuGet 套件快取。 - 刪除專案的
bin
和obj
資料夾。 - 還原並重建專案。
- 在重新部署應用程式之前,請先刪除伺服器上部署資料夾中的所有檔案。
注意
不支援使用與應用程式目標框架不相容的套件版本。 如需套件的詳細資訊,請使用 NuGet 資源庫或 FuGet 套件總管。
執行伺服器應用程式
測試 Blazor Web App並且進行疑難排解時,請確定您是從伺服器專案執行應用程式。
檢查使用者
下列 UserClaims
元件可以直接在應用程式中使用,或作為進一步自訂的基礎。
UserClaims.razor
:
@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>User Claims</PageTitle>
<h1>User Claims</h1>
@if (claims.Any())
{
<ul>
@foreach (var claim in claims)
{
<li><b>@claim.Type:</b> @claim.Value</li>
}
</ul>
}
@code {
private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();
[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; }
protected override async Task OnInitializedAsync()
{
if (AuthState == null)
{
return;
}
var authState = await AuthState;
claims = authState.User.Claims;
}
}
其他資源
- Microsoft identity 平台文件
AzureAD/microsoft-identity-web
GitHub 存放庫:實作 Microsoft Identity Web for Microsoft Entra ID 和 Azure Active Directory B2C for ASP.NET Core 應用程式的實用指導,包括應用程式範例和相關 Azure 文件的連結。 目前,Blazor Web App 並未由 Azure 文件明確解決,但適用於 ME-ID 和 Azure 裝載之 Blazor Web App 的安裝和設定,與任何 ASP.NET Core Web 應用程式相同。AuthenticationStateProvider
service- 在 Blazor Web App管理驗證狀態
- 中的 Blazor Web App服務抽象概念