共用方式為


保護 ASP.NET Core 伺服器端 Blazor 應用程式

注意

這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前的版本,請參閱 本文的 .NET 9 版本。

本文說明如何以 ASP.NET Core 應用程式的形式保護伺服器端 Blazor 應用程式。

為求安全,伺服器端 Blazor 應用程式的設定方式會與 ASP.NET Core 應用程式相同。 如需詳細資訊,請參閱 ASP.NET Core 安全性主題底下的文章。

只有在應用程式啟動時 (也就是應用程式第一次連線到 WebSocket 時) 才會建立驗證內容。 驗證內容會在線路的存留期內受到維護。 應用程式會每隔 30 分鐘,定期重新驗證使用者的驗證狀態。

如果應用程式必須擷取自訂服務的使用者,或回應使用者更新,請參閱伺服器端 ASP.NET Core Blazor 其他安全性案例

Blazor 不同於傳統伺服器轉譯的 Web 應用程式,這些應用程式會在瀏覽每個分頁時,使用 Cookie 提出新的 HTTP 要求。 瀏覽事件期間會檢查驗證。 不過,不涉及 Cookie。 只有在對伺服器提出 HTTP 要求時,才會傳送 Cookie,這不是使用者在 Blazor 應用程式中瀏覽時發生的情況。 在瀏覽期間,會在線路內 Blazor 檢查使用者的驗證狀態,您可以使用重新驗證 AuthenticationStateProvider](#additional-authentication-state-providers)隨時更新伺服器上的驗證狀態。

重要

不建議在瀏覽期間實作自訂 NavigationManager 來實現驗證確認。 如果應用程式必須在瀏覽期間執行自訂驗證狀態邏輯,請使用自訂 AuthenticationStateProvider

注意

本文中的程式碼範例採用 可為 Null 的參考型別 (NRT) 和 .NET 編譯器 Null 狀態靜態分析,這在 .NET 6 或更新版本的 ASP.NET Core 中受到支援。 以 ASP.NET Core 5.0 或更早版本為目標時,請從本文的範例中移除 Null 型別指定 (?)。

敏感數據和認證的伺服器端安全性

在測試/預備和生產環境中,伺服器端 Blazor 程序代碼和 Web API 應該使用安全驗證流程,以避免在專案程式代碼或組態檔內維護認證。 在本機開發測試之外,建議您避免使用環境變數來儲存敏感數據,因為環境變數不是最安全的方法。 針對本機開發測試, 建議使用秘密管理員工具 來保護敏感數據。 如需詳細資訊,請參閱以下資源:

針對客戶端和伺服器端本機開發和測試,請使用 秘密管理員工具來 保護敏感性認證。

專案範本

遵循 Tooling for ASP.NET Core Blazor 中的指引,建立新的伺服器端 Blazor 應用程式。

在選擇伺服器端應用程式範本並設定專案之後,請在 [驗證類型] 底下選取應用程式的驗證:

  • (預設值):無驗證。
  • 個別帳戶:使用者帳戶會使用 ASP.NET Core Identity 儲存在應用程式內。
  • (預設值):無驗證。
  • 個別帳戶:使用者帳戶會使用 ASP.NET Core Identity 儲存在應用程式內。
  • Microsoft identity 平台:如需詳細資訊,請參閱 ASP.NET Core Blazor驗證和授權
  • Windows:使用 Windows 驗證。

BlazorIdentity UI (個人帳戶)

當您選擇 [個別帳戶] 的驗證選項時,Blazor 支援產生完整 Blazor 型 Identity UI。

Blazor Web App 範本會為 SQL Server 資料庫產生 Identity 程式碼。 命令列版本會使用 SQLite,並包含 Identity 的 SQLite 資料庫。

範本:

  • 有支援互動式伺服器端轉譯 (互動式 SSR),還有經驗證使用者的用戶端轉譯 (CSR) 案例。
  • 新增 IdentityRazor 例行驗證工作的元件和相關邏輯,例如:登入和登出的使用者。這些 Identity 元件也支援進階 Identity 功能,例如使用第三方應用程式進行帳戶確認和密碼復原,還有多重要素驗證 。 請注意 Identity 元件本身暫不支持互動功能。
  • 新增 Identity 相關的套件和相依性。
  • 參考 _Imports.razor 中的 Identity 套件。
  • 建立自訂使用者 Identity 類別 (ApplicationUser)。
  • 建立及註冊 EF Core 資料庫內容 (ApplicationDbContext)。
  • 設定內建 Identity 端點的路由。
  • 包含 Identity 驗證和商務邏輯。

若要檢查 Blazor 架構的 Identity 元件,請到PagesShared 資料夾中存取,存放在Account 這個資料夾中,資料就存放在 Blazor Web App 專案範本 (參考來源) 那邊

當您選擇互動式 WebAssembly 或互動式自動轉譯模式時,伺服器會處理所有驗證和授權要求,而 Identity 元件會在 Blazor Web App 的主要專案中透過靜態方式,呈現在伺服器上。

架構會在伺服器和用戶端 (.Client) 專案中提供自訂 AuthenticationStateProvider,以將使用者的驗證狀態流向瀏覽器。 伺服器專案會呼叫 AddAuthenticationStateSerialization,而用戶端專案會呼叫 AddAuthenticationStateDeserialization。 在伺服器上進行驗證,而不是用戶端允許應用程式在預先轉譯期間,以及在初始化 NET WebAssembly 執行階段之前存取驗證狀態。 自訂 AuthenticationStateProvider 實作會使用保存元件狀態服務 (PersistentComponentState) 將驗證狀態序列化為 HTML 註解,然後從 WebAssembly 讀取它以建立新的 AuthenticationState 執行個體。 如需詳細資訊,請參閱在 Blazor Web App 中管理驗證狀態這個章節。

僅適用於互動式伺服器解決方案,IdentityRevalidatingAuthenticationStateProvider (參考來源) 是伺服器端 AuthenticationStateProvider,連線互動式線路時,每 30 分鐘會重新驗證已連線使用者的安全性戳記。

當您選擇互動式 WebAssembly 或互動式自動轉譯模式時,伺服器會處理所有驗證和授權要求,而 Identity 元件會在 Blazor Web App 的主要專案中透過靜態方式,呈現在伺服器上。 專案範本包含 .Client 專案中的 PersistentAuthenticationStateProvider 類別 (參考來源),以同步處理伺服器與瀏覽器之間的使用者驗證狀態。 類別是 AuthenticationStateProvider 的自訂實作。 提供者會使用保存元件狀態服務 (PersistentComponentState),預先轉譯驗證狀態,並將其保存至頁面。

在 Blazor Web App 的主要專案中,驗證狀態供應器名為 IdentityRevalidatingAuthenticationStateProvider(參考來源)(僅限伺服器互動功能解決方案)或 PersistingRevalidatingAuthenticationStateProvider (參考來源)(WebAssembly 或自動互動功能解決方案)。

BlazorIdentity 取決於 DbContext 執行個體未由處理站建立,這是刻意的,因為 DbContext 足以讓專案範本的 Identity 元件以靜態方式轉譯,而不用支援互動功能。

如需在針對 Identity 元件強制執行靜態 SSR 的同時,全域互動轉譯模式如何套用至非 Identity 元件的描述,請參閱 ASP.NET Core Blazor 轉譯模式

如需保存預先轉譯狀態的詳細資訊,請參閱預先轉譯 ASP.NET Core Razor 元件

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

在  Blazor Web App 管理驗證狀態

本章節適用於 Blazor Web App會採用以下幾點:

  • 個人帳戶
  • 用戶端轉譯 (CSR,WebAssembly 型互動)。

用戶端驗證狀態供應器僅在 Blazor 內使用,不會與 ASP.NET Core 驗證系統整合。 在預先轉譯期間,Blazor 遵守頁面上定義的中繼資料,並且使用 ASP.NET Core 驗證系統來判斷使用者是否已驗證。 當使用者從一個頁面瀏覽到另一個頁面時,會使用用戶端驗證提供者。 當使用者重新整理頁面 (完整頁面重新載入) 時,用戶端驗證狀態供應器不會參與伺服器上的驗證決策。 由於伺服器不會保存使用者的狀態,因此維護用戶端的任何驗證狀態都會遺失。

若要解決此問題,最佳方法是在 ASP.NET Core 驗證系統中執行驗證。 用戶端驗證狀態供應器只會負責反映使用者的驗證狀態。 Blazor Web App 專案範本示範如何使用驗證狀態供應器來完成這項作業的範例,分述如下。

在伺服器專案的 Program 檔案中,呼叫 AddAuthenticationStateSerialization,其會使用保存元件狀態服務 (PersistentComponentState) 序列化伺服器端 AuthenticationStateProvider 傳回的 AuthenticationState

builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents()
    .AddAuthenticationStateSerialization();

這些 API 只會序列化伺服器端名稱和角色宣告,以在瀏覽器中存取。 若要包含所有宣告,請將 SerializeAllClaims 設定為在伺服器端呼叫 AddAuthenticationStateSerializationtrue

builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents()
    .AddAuthenticationStateSerialization(
        options => options.SerializeAllClaims = true);

在用戶端 (.Client) 專案的 Program 檔案中,呼叫 AddAuthenticationStateDeserialization,這會新增 AuthenticationStateProvider,其中會使用 AuthenticationStateData保存元件狀態服務 (PersistentComponentState) 從伺服器還原序列化 AuthenticationState。 伺服器專案中應該有對 AddAuthenticationStateSerialization 的對應呼叫。

builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthenticationStateDeserialization();

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

產生Identity

如需如何將 Identity 產生至伺服器端 Blazor 應用程式的詳細資訊,請參閱在 ASP.NET Core 專案中產生 Identity

將 Identity 產生至伺服器端 Blazor 應用程式:

來自外部提供者的其他宣告和權杖

若要儲存來自外部提供者的其他宣告,請參閱在 ASP.NET Core 中保存來自外部提供者的其他宣告和權杖

Linux 上的 Azure App Service 與 Identity 伺服器

請在部署 Linux 上的 Azure App Service 與 Identity 伺服器時明確指定簽發者。 如需詳細資訊,請參閱使用 Identity 來保護 SPA 的 Web API 後端

為範圍限定為元件的服務插入 AuthenticationStateProvider

請不要嘗試在自訂範圍內解析 AuthenticationStateProvider,因為其會導致為未正確初始化的 AuthenticationStateProvider 建立新執行個體。

若要在範圍限定為元件的服務內存取 AuthenticationStateProvider,請使用 @inject 指示詞[Inject] 屬性插入 AuthenticationStateProvider,並將其以參數形式傳遞至服務。 此方法確保針對每個使用者應用程式執行個體使用正確、初始化的 AuthenticationStateProvider 執行個體。

ExampleService.cs

public class ExampleService
{
    public async Task<string> ExampleMethod(AuthenticationStateProvider authStateProvider)
    {
        var authState = await authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            return $"{user.Identity.Name} is authenticated.";
        }
        else
        {
            return "The user is NOT authenticated.";
        }
    }
}

將服務註冊為範圍。 在伺服器端 Blazor 應用程式中,限定範圍服務的存留期等於用戶端連線線路的持續時間。

Program 檔案中:

builder.Services.AddScoped<ExampleService>();

Startup.csStartup.ConfigureServices 中:

services.AddScoped<ExampleService>();

在下列 InjectAuthStateProvider 元件中:

InjectAuthStateProvider.razor

@page "/inject-auth-state-provider"
@inherits OwningComponentBase
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>Inject <code>AuthenticationStateProvider</code> Example</h1>

<p>@message</p>

@code {
    private string? message;
    private ExampleService? ExampleService { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ExampleService = ScopedServices.GetRequiredService<ExampleService>();

        message = await ExampleService.ExampleMethod(AuthenticationStateProvider);
    }
}
@page "/inject-auth-state-provider"
@inject AuthenticationStateProvider AuthenticationStateProvider
@inherits OwningComponentBase

<h1>Inject <code>AuthenticationStateProvider</code> Example</h1>

<p>@message</p>

@code {
    private string? message;
    private ExampleService? ExampleService { get; set; }

    protected override async Task OnInitializedAsync()
    {
        ExampleService = ScopedServices.GetRequiredService<ExampleService>();

        message = await ExampleService.ExampleMethod(AuthenticationStateProvider);
    }
}

如需詳細資訊,請參閱 ASP.NET Core Blazor 相依性插入OwningComponentBase 的相關指引。

使用自訂 AuthenticationStateProvider 預先轉譯時會顯示未經授權的內容

若要避免在預先轉譯自訂 AuthenticationStateProvider 時顯示未經驗證的內容,例如 AuthorizeView 元件中的內容,請採用以下其中一個方法:

  • 停用預先轉譯:指出轉譯模式,其中 prerender 參數在應用程式元件階層的最高階元件中設定為 false,最高階元件不是根元件。

    注意

    不支援讓根元件成為互動式,例如 App 元件。 因此,App 元件無法直接停用預先轉譯。

    針對以 Blazor Web App 專案範本為基礎的應用程式,通常會先停用預先轉譯,其中 Routes 元件的使用時機是應用到 App 元件 (Components/App.razor) 上:

    <Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
    

    此外,停用元件的 HeadOutlet 預先轉譯:

    <HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
    

    您也可以選擇性地控制套用至 Routes 元件執行個體的轉譯模式。 如需範例,請參閱 ASP.NET Core Blazor 轉譯模式

  • 停用預先轉譯:開啟 _Host.cshtml 檔案,並將元件標籤協助程式render-mode 屬性變更為 Server

    <component type="typeof(App)" render-mode="Server" />
    
  • 在應用程式啟動前驗證伺服器上的使用者:若要採用這種方法,應用程式必須以 Identity 型登入頁面或檢視回應使用者的初始要求,並防止對 Blazor 端點的任何要求,直到其驗證完畢為止。 如需詳細資訊,請參閱使用受授權保護的使用者資料建立 ASP.NET Core 應用程式。 驗證之後,只有在使用者真正未經授權檢視內容時,才會顯示預先轉譯 Razor 元件中未經授權的內容。

使用者狀態管理

儘管名稱中有 "state" 這個字,但 AuthenticationStateProvider 不是用於儲存「一般使用者狀態」AuthenticationStateProvider 只會指出使用者對應用程式的驗證狀態、其是否登入應用程式,以及其以什麼身分登入。

驗證會使用與 Razor Pages 和 MVC 應用程式相同的 ASP.NET Core Identity 驗證。 針對 ASP.NET Core Identity 儲存的使用者狀態會流向 Blazor,而無需將額外的程式碼新增至應用程式。 請遵循 ASP.NET Core Identity 文章和教學課程中的指引,讓 Identity 功能在應用程式的 Blazor 組件中生效。

如需 ASP.NET Core Identity 以外的一般狀態管理指引,請參閱 ASP.NET Core Blazor 狀態管理

其他驗證狀態提供者

衍生自 AuthenticationStateProvider 管理伺服器上驗證狀態的兩個額外類別:

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

登出時的驗證狀態管理

伺服器端 Blazor 會在線路存留期內保存使用者驗證狀態,包括跨瀏覽器索引標籤。 當使用者在某一索引標籤登出時,若要跨瀏覽器索引標籤主動將使用者登出,您必須使用簡短的 RevalidationInterval 來實作 RevalidatingServerAuthenticationStateProvider (參考來源)。

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

暫時重新導向 URL 有效期間

本章節適用於 Blazor Web App。

針對 Blazor 伺服器端轉譯發出的暫時重新導向 URL,使用 RazorComponentsServiceOptions.TemporaryRedirectionUrlValidityDuration 選項來取得或設定 ASP.NET Core 資料保護有效性的存留期。 這些只是暫時使用,因此存留期只要足夠讓用戶端接收 URL 並開始瀏覽至該 URL 即可。 不過,也應該足以允許跨伺服器時發生的時鐘誤差。 預設值為五分鐘。

在下列範例中,該值會延長至七分鐘:

builder.Services.AddRazorComponents(options => 
    options.TemporaryRedirectionUrlValidityDuration = 
        TimeSpan.FromMinutes(7));

其他資源