ASP.NET Core Blazor 驗證與授權

注意

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

重要

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

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

本文說明 ASP.NET Core 對於在 Blazor 應用程式中設定及管理安全性的支援。

在 Blazor 應用程式中,伺服器端和用戶端所執行的授權碼是不同的安全性情境。 若是在伺服器上執行的授權碼,授權檢查可針對應用程式和元件的區域強制執行存取規則。 由於用戶端的程式碼執行可能會遭到竄改,因此無法保證在用戶端上執行的授權碼會絕對強制執行存取規則或控制用戶端內容的顯示。

如果必須確保授權規則的強制執行,請勿將授權檢查實作於用戶端程式碼中。 請建置只依賴伺服器端轉譯 (SSR) 進行授權檢查和規則強制執行的 Blazor Web 應用程式。

Razor Pages 授權慣例不會套用至路由式 Razor 元件。 如果非路由 Razor 元件內嵌在 Razor[頁面]應用程式 的頁面中,頁面的授權慣例會間接影響 Razor 元件以及其餘頁面內容。

如果必須確保授權規則強制執行以及資料與程式碼的安全性,請勿開發用戶端應用程式。 建置 Blazor Server 應用程式。

Razor Pages 授權慣例不會套用至路由式 Razor 元件。 如果非路由 Razor 元件內嵌在 Razor[頁面]應用程式 的頁面中,頁面的授權慣例會間接影響 Razor 元件以及其餘頁面內容。

ASP.NET Core Identity 的設計目的是要在 HTTP 要求和回應通訊的情境中運作,而這通常並不是 Blazor 應用程式的用戶端-伺服器通訊模型。 使用 ASP.NET Core Identity 來管理使用者的 ASP.NET Core 應用程式,應該使用 Razor Pages 而非 Razor 元件來取得 Identity 相關 UI,例如使用者註冊、登入、登出和其他使用者管理工作。 建置直接處理 Identity 工作的 Razor 元件在數個案例中是可行的,但 Microsoft 不建議這麽做亦不提供支援。

Razor 元件不支援 ASP.NET Core 抽象概念,例如 SignInManager<TUser>UserManager<TUser>。 如需搭配使用 ASP.NET Core Identity 與 Blazor 的詳細資訊,請參閱 將 ASP.NET Core Identity 搭建到伺服器端 Blazor應用程式中

注意

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

Antiforgery 支援

Blazor 範本:

AntiforgeryToken 元件會將 Antiforgery 權杖轉譯為隱藏欄位,而此元件會自動新增至表單 (EditForm) 執行個體。 如需詳細資訊,請參閱 ASP.NET Core Blazor 表單概觀

AntiforgeryStateProvider 服務會提供與目前工作階段相關聯之 Antiforgery 權杖的存取權。 插入服務並呼叫其 GetAntiforgeryToken() 方法來取得目前的 AntiforgeryRequestToken。 如需詳細資訊,請參閱 從 ASP.NET Core Blazor 應用程式呼叫 Web API

Blazor 會將要求權杖儲存在元件狀態中,這可確保互動式元件可以使用 Antiforgery 權杖,即使它們沒有要求的存取權也一樣。

注意

只有在將表單資料提交至編碼為 application/x-www-form-urlencodedmultipart/form-datatext/plain 的伺服器時,才需要防偽移轉 (部分機器翻譯),因為只有這些是有效的表單編碼類型 (英文)。

如需詳細資訊,請參閱以下資源:

驗證

Blazor 會使用現有的 ASP.NET Core 驗證機制來建立使用者的身分識別。 確切的機制會取決於 Blazor 應用程式的裝載方式 (伺服器端或用戶端)。

伺服器端 Blazor 驗證

以互動方式轉譯的伺服器端 Blazor 會透過與用戶端的 SignalR 連線運作。 SignalR 型應用程式中的驗證會在連線建立時進行處理。 驗證可以依據 cookie 或其他持有人權杖,但驗證會透過 SignalR 中樞並完全在 線路 內進行管理。

內建的 AuthenticationStateProvider 服務會從 ASP.NET Core 的 HttpContext.User 取得驗證狀態。 這就是驗證狀態與現有 ASP.NET Core 驗證機制之間的整合方式。

Razor 元件中的 IHttpContextAccessor/HttpContext

IHttpContextAccessor 必須避免使用互動式轉譯,因為沒有有效的 HttpContext 可用。

IHttpContextAccessor 可用於伺服器上靜態轉譯的元件。 不過,建議您盡可能避免。

只能在一般工作的靜態轉譯根元件中將 HttpContext 用作 級聯參數,例如檢查和修改 App 元件 (Components/App.razor) 中的標頭或其他屬性。 用於互動式轉譯的值一律是 null

[CascadingParameter]
public HttpContext? HttpContext { get; set; }

針對互動式元件中所需 HttpContext 的案例,建議您透過伺服器的持續元件狀態來流動資料。 如需詳細資訊,請參閱 伺服器端 ASP.NET Core Blazor的其他安全性案例

請勿在伺服器端 Blazor 應用程式的 Razor 元件中直接或間接使用 IHttpContextAccessor/HttpContext Blazor 應用程式會在 ASP.NET Core 管線內容之外執行。 既不保證 HttpContextIHttpContextAccessor 中可用,也不保證 HttpContext 會保留啟動了 Blazor 應用程式的內容。

建議在 Blazor 應用程式的初始轉譯期間,透過根元件參數將要求狀態傳遞給此應用程式。 或者,應用程式可以將資料複製到根元件初始化生命週期事件中的範圍服務,以便在整個應用程式中使用。 如需詳細資訊,請參閱伺服器端 ASP.NET Core Blazor 其他安全性案例

伺服器端 Blazor 安全性的一個重要層面是,附加至指定線路的使用者可能會在建立 Blazor 線路後的某個時間點進行更新,但 IHttpContextAccessor 不會更新。 如需使用自訂服務解決這種情況的詳細資訊,請參閱 伺服器端 ASP.NET CoreBlazor 其他安全性案例

共用狀態

伺服器端 Blazor 應用程式位於伺服器記憶體中,且多個應用程式工作階段會裝載在相同的處理程序內。 針對每個應用程式工作階段,Blazor 會啟動具有其本身相依性插入容器範圍的線路,因此每個 Blazor 工作階段的範圍服務都是唯一的。

警告

除非非常小心,否則不建議使用單一資料庫服務在相同伺服器共用狀態上的應用程式,因為這可能會造成安全性弱點,例如跨線路洩漏使用者狀態。

如果應用程式特別針對它所設計,您可以在 Blazor 應用程式中使用具狀態單一資料庫服務。 例如,使用單一資料庫記憶體快取是可接受的,因為記憶體快取需要索引鍵才能存取指定的項目。 假設使用者無法控制與快取搭配使用的快取索引鍵,則儲存在快取中的狀態不會跨線路洩漏。

如需狀態管理的一般指導,請參閱 ASP.NET Core Blazor 狀態管理

用戶端端 Blazor 驗證

在用戶端 Blazor 應用程式中,用戶端驗證檢查可以被略過,因為使用者可以修改所有用戶端端程式碼。 這同樣也適用於所有的用戶端端的應用程式技術,包括 JavaScript SPA 架構或任何作業系統的原生應用程式。

新增下列項目:

若要處理驗證,下列章節會說明如何使用內建或自訂的 AuthenticationStateProvider 服務。

如需詳細資訊,請參閱 保護 ASP.NET Core 的安全Blazor WebAssembly

AuthenticationStateProvider 服務

AuthenticationStateProviderAuthorizeView 元件和串聯驗證服務用來取得使用者驗證狀態的基礎服務。

AuthenticationStateProviderAuthorizeView 元件與 CascadingAuthenticationState 元件用來取得使用者驗證狀態的基礎服務。

您通常不會直接使用 AuthenticationStateProvider。 請使用本文稍後所述的 AuthorizeView 元件Task<AuthenticationState> 方法。 使用 AuthenticationStateProvider 的主要缺點,在於系統不會在基礎驗證狀態資料變更時自動通知該元件。

AuthenticationStateProvider 服務可以提供目前使用者的 ClaimsPrincipal 資料,如下列範例所示。

ClaimsPrincipalData.razor

@page "/claims-principle-data"
@using System.Security.Claims
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>ClaimsPrincipal Data</h1>

<button @onclick="GetClaimsPrincipalData">Get ClaimsPrincipal Data</button>

<p>@authMessage</p>

@if (claims.Count() > 0)
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}

<p>@surname</p>

@code {
    private string? authMessage;
    private string? surname;
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    private async Task GetClaimsPrincipalData()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            authMessage = $"{user.Identity.Name} is authenticated.";
            claims = user.Claims;
            surname = user.FindFirst(c => c.Type == ClaimTypes.Surname)?.Value;
        }
        else
        {
            authMessage = "The user is NOT authenticated.";
        }
    }
}

在前述範例中:

@page "/claims-principle-data"
@using System.Security.Claims
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>ClaimsPrincipal Data</h1>

<button @onclick="GetClaimsPrincipalData">Get ClaimsPrincipal Data</button>

<p>@authMessage</p>

@if (claims.Count() > 0)
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}

<p>@surname</p>

@code {
    private string? authMessage;
    private string? surname;
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    private async Task GetClaimsPrincipalData()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            authMessage = $"{user.Identity.Name} is authenticated.";
            claims = user.Claims;
            surname = user.FindFirst(c => c.Type == ClaimTypes.Surname)?.Value;
        }
        else
        {
            authMessage = "The user is NOT authenticated.";
        }
    }
}

如果 user.Identity.IsAuthenticatedtrue,且由於使用者為 ClaimsPrincipal,系統便可以列舉宣告,並評估角色中的成員資格。

如需相依性插入 (DI) 和服務的詳細資訊,請參閱 ASP.NET Core Blazor 相依性插入ASP.NET Core 中的相依性插入。 如需如何在伺服器端 Blazor 應用程式中實作自訂 AuthenticationStateProvider 的相關資訊,請參閱 保護 ASP.NET Core 伺服器端Blazor 應用程式

將驗證狀態公開為階層式參數

如果程序性邏輯需要驗證狀態資料 (例如在執行由使用者觸發的動作時),請透過定義 Task<AuthenticationState> 類型的階層式參數來取得驗證狀態資料,如下列範例所示。

CascadeAuthState.razor

@page "/cascade-auth-state"

<h1>Cascade Auth State</h1>

<p>@authMessage</p>

@code {
    private string authMessage = "The user is NOT authenticated.";

    [CascadingParameter]
    private Task<AuthenticationState>? authenticationState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (authenticationState is not null)
        {
            var authState = await authenticationState;
            var user = authState?.User;

            if (user?.Identity is not null && user.Identity.IsAuthenticated)
            {
                authMessage = $"{user.Identity.Name} is authenticated.";
            }
        }
    }
}
@page "/cascade-auth-state"

<h1>Cascade Auth State</h1>

<p>@authMessage</p>

@code {
    private string authMessage = "The user is NOT authenticated.";

    [CascadingParameter]
    private Task<AuthenticationState>? authenticationState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (authenticationState is not null)
        {
            var authState = await authenticationState;
            var user = authState?.User;

            if (user?.Identity is not null && user.Identity.IsAuthenticated)
            {
                authMessage = $"{user.Identity.Name} is authenticated.";
            }
        }
    }
}

如果 user.Identity.IsAuthenticatedtrue,系統便可以列舉宣告,並評估角色中的成員資格。

使用 AuthorizeRouteView 和串聯驗證狀態服務設定 Task<AuthenticationState> 串聯參數

當您從其中一個已啟用驗證的 Blazor 專案範本建立 Blazor 應用程式時,此應用程式會包含上述範例中顯示的 AuthorizeRouteView 以及對 AddCascadingAuthenticationState 的呼叫。 用戶端端 Blazor 應用程式也包含必要的服務註冊。 使用路由器元件自訂未經授權的內容 章節中會顯示其他資訊。

<Router ...>
    <Found ...>
        <AuthorizeRouteView RouteData="routeData" 
            DefaultLayout="typeof(Layout.MainLayout)" />
        ...
    </Found>
</Router>

Program 檔案中,註冊串聯驗證狀態服務:

builder.Services.AddCascadingAuthenticationState();

使用 AuthorizeRouteViewCascadingAuthenticationState 元件設定 Task<AuthenticationState>階層式參數

當您從其中一個已啟用驗證的 Blazor 專案範本建立 Blazor 應用程式時,此應用程式會包含上述範例中顯示的 AuthorizeRouteViewCascadingAuthenticationState 元件。 用戶端端 Blazor 應用程式也包含必要的服務註冊。 使用路由器元件自訂未經授權的內容 章節中會顯示其他資訊。

<CascadingAuthenticationState>
    <Router ...>
        <Found ...>
            <AuthorizeRouteView RouteData="routeData" 
                DefaultLayout="typeof(MainLayout)" />
            ...
        </Found>
    </Router>
</CascadingAuthenticationState>

注意

隨著 ASP.NET Core 5.0.1 的發行以及在任何其他 5.x 版中,Router 元件會包含設定為 @truePreferExactMatches 參數。 如需詳細資訊,請參閱從 ASP.NET Core 3.1 移轉至 5.0

在用戶端端 Blazor 應用程式中,將選項和授權的服務新增至 Program 檔案:

builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();

在伺服器端 Blazor 應用程式中,選項和授權的服務已經存在,因此不需要再採取進一步動作。

授權

在使用者被驗證之後,系統便會套用「授權」規則以控制使用者可以執行的動作。

存取權通常會依據下列情況來授與或拒絕:

  • 使用者已通過驗證 (已登入)。
  • 使用者屬於某個「角色」
  • 使用者具有「宣告」
  • 已滿足某個「原則」

上述概念皆和 ASP.NET Core MVC 或 Razor Pages 應用程式中的概念相同。 如需 ASP.NET Core 安全性的詳細資訊,請參閱 ASP.NET Core 安全性與Identity下的文章。

AuthorizeView 元件

AuthorizeView 元件會根據使用者是否獲得授權來選擇性地顯示 UI 內容。 此方法可讓您只需要向使用者「顯示」資料,而不需要在程序性邏輯中使用該使用者的身分識別。

該元件會公開 AuthenticationState 類型的 context 變數 (Razor 語法中的 @context),您可以使用它來存取已登入使用者的相關資訊:

<AuthorizeView>
    <p>Hello, @context.User.Identity?.Name!</p>
</AuthorizeView>

您可以也透過 AuthorizedNotAuthorized 參數的組合,根據使用者獲得授權與否來提供不同的顯示內容:

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
        <p><button @onclick="SecureMethod">Authorized Only Button</button></p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

@code {
    private void SecureMethod() { ... }
}

已授權元素的預設事件處理常式 (例如上述範例中 <button> 元素的 SecureMethod 方法) 只能由已授權的使用者叫用。

當靜態伺服器端轉譯 (靜態 SSR) 期間授權失敗時,Blazor Web Apps 的 Razor 元件一律不會顯示 <NotAuthorized> 內容。 伺服器端 ASP.NET Core 管線會處理伺服器上的授權。 使用伺服器端技術來處理未經授權的要求。 如需詳細資訊,請參閱 ASP.NET Core Blazor 轉譯模式

警告

AuthorizeView 相關聯的用戶端端標記和方法只會受到保護,無法在用戶端端應用程式 Blazor 中 轉譯的 UI 中檢視和執行。 為了保護用戶端端 Blazor 中授權的內容和安全方法,內容通常是由安全、授權的 Web API 呼叫提供給伺服器 API,且永遠不會儲存在應用程式中。 如需詳細資訊,請參閱 從 ASP.NET Core Blazor應用程式 呼叫 Web API 和 ASP.NET CoreBlazor WebAssembly 其他安全性案例

AuthorizedNotAuthorized 的內容可以包含任意項目,例如其他互動式元件。

授權情況 (例如控制 UI 選項或存取的角色或原則) 已涵蓋於授權一節。

如果未指定授權條件,AuthorizeView 會使用預設的原則:

  • 已驗證 (已登入) 的使用者視為已授權。
  • 未驗證 (已登出) 的使用者視為未授權。

AuthorizeView 元件可用於 NavMenu 元件 (Shared/NavMenu.razor) 以顯示 NavLink 元件 (NavLink),但請注意,此方法只會從轉譯的輸出中移除清單項目。 其無法防止使用者瀏覽至元件。 在目的地元件中個別實作授權。

角色型和原則型授權

AuthorizeView 元件支援「角色型」或「原則型」授權。

針對角色型授權,請使用 Roles 參數。 在下列範例中,使用者必須具有 AdminSuperuser 角色的角色宣告:

<AuthorizeView Roles="Admin, Superuser">
    <p>You have an 'Admin' or 'Superuser' role claim.</p>
</AuthorizeView>

若要要求使用者同時具有 AdminSuperuser 角色宣告,請巢狀 AuthorizeView 元件:

<AuthorizeView Roles="Admin">
    <p>User: @context.User</p>
    <p>You have the 'Admin' role claim.</p>
    <AuthorizeView Roles="Superuser" Context="innerContext">
        <p>User: @innerContext.User</p>
        <p>You have both 'Admin' and 'Superuser' role claims.</p>
    </AuthorizeView>
</AuthorizeView>

上述程式碼會為內部 AuthorizeView 元件建立 Context,以防止 AuthenticationState 內容衝突。 AuthenticationState 內容會在外部 AuthorizeView 使用存取內容的標準方法 (@context.User) 存取。 內容會在內部 AuthorizeView 使用具名 innerContext 內容 (@innerContext.User) 存取。

如需詳細資訊,包括設定指引,請參閱 ASP.NET Core 中的角色型授權

針對原則型授權,請使用有單一原則的 Policy 參數:

<AuthorizeView Policy="Over21">
    <p>You satisfy the 'Over21' policy.</p>
</AuthorizeView>

若要處理使用者應該滿足數個原則之一的情況,請建立原則,確認使用者符合其他原則。

若要處理使用者必須同時滿足數個原則的情況,請採取 下列其中一種 方法:

  • 建立 AuthorizeView 的原則,確認使用者符合數個其他原則。

  • 在多個 AuthorizeView 元件中巢狀原則:

    <AuthorizeView Policy="Over21">
        <AuthorizeView Policy="LivesInCalifornia">
            <p>You satisfy the 'Over21' and 'LivesInCalifornia' policies.</p>
        </AuthorizeView>
    </AuthorizeView>
    

宣告型授權是特殊案例的原則型授權。 例如,您可以定義要求使用者具備特定宣告的原則。 如需詳細資訊,請參閱 ASP.NET Core 中的原則型授權

如果未指定 RolesPolicyAuthorizeView 便會使用預設原則:

  • 已驗證 (已登入) 的使用者視為已授權。
  • 未驗證 (已登出) 的使用者視為未授權。

因為 .NET 字串比較預設會區分大小寫,因此比對角色和原則名稱也會區分大小寫。 例如,Admin (大寫 A) 不會被視為與 admin (小寫 a) 相同的角色。

Pascal 大小寫通常用於角色和原則名稱 (例如 BillingAdministrator),但使用 Pascal 大小寫並非嚴格的需求。 允許不同的大小寫方案,如駝峰式大小寫、肉串式大小寫和蛇式大小寫。 在角色和原則名稱中使用空格不尋常,但架構允許。 例如,billing administrator 在 .NET 應用程式中雖是不尋常的角色或原則名稱格式,但是個有效的角色或原則名稱。

在非同步驗證期間所顯示的內容

Blazor 允許以「非同步」方式判斷驗證狀態。 此方法的主要案例是會向外部端點要求驗證的用戶端端 Blazor 應用程式。

在驗證期間,AuthorizeView 預設不會顯示任何內容。 若要在驗證進行時顯示內容,請將內容指派給 Authorizing 參數:

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
    </Authorized>
    <Authorizing>
        <p>You can only see this content while authentication is in progress.</p>
    </Authorizing>
</AuthorizeView>

此方法通常不適用於伺服器端 Blazor 應用程式。 伺服器端 Blazor 應用程式在驗證狀態建立時,便能立即得知該狀態。 可以在應用程式的 AuthorizeView 元件中提供 Authorizing 內容,但該內容永遠不會顯示。

[Authorize] 屬性

可在 Razor 元件中使用[Authorize]屬性:

@page "/"
@attribute [Authorize]

You can only see this if you're signed in.

重要

請僅在透過 Blazor 路由器抵達的 @page 元件上使用 [Authorize]。 授權僅會以路由的層面執行,且「不」適用於在頁面內轉譯的子元件。 若要授權在頁面內顯示特定組件,請改為使用 AuthorizeView

[Authorize] 屬性也支援角色型或原則型授權。 針對角色型授權,請使用 Roles 參數:

@page "/"
@attribute [Authorize(Roles = "Admin, Superuser")]

<p>You can only see this if you're in the 'Admin' or 'Superuser' role.</p>

針對原則型授權,請使用 Policy 參數:

@page "/"
@attribute [Authorize(Policy = "Over21")]

<p>You can only see this if you satisfy the 'Over21' policy.</p>

如果未指定 RolesPolicy[Authorize] 便會使用預設原則:

  • 已驗證 (已登入) 的使用者視為已授權。
  • 未驗證 (已登出) 的使用者視為未授權。

當使用者未獲得授權,且應用程式未 使用路由器元件自訂未經授權的內容時,架構會自動顯示下列後援訊息:

Not authorized.

資源授權

若要授權使用者使用資源,請將要求的路由資料傳遞至 AuthorizeRouteViewResource 參數。

在要求路由的 Router.Found 內容中:

<AuthorizeRouteView Resource="routeData" RouteData="routeData" 
    DefaultLayout="typeof(MainLayout)" />

如需如何在程序邏輯中傳遞及使用授權狀態資料的詳細資訊,請參閱將驗證狀態公開為串聯參數一節。

AuthorizeRouteView 收到資源的路由資料時,授權原則可以存取 RouteData.PageTypeRouteData.RouteValues 以讓自訂邏輯決定是否授權。

下列範例會使用下列邏輯,在 AuthorizationOptions 中為應用程式的授權服務設定 (AddAuthorizationCore) 建立 EditUser 原則:

  • 判斷是否有索引鍵為 id 的路由值存在。 如果該索引鍵存在,路由值便會儲存到 value 中。
  • 在名為 id 的變數中,將 value 儲存為字串,或設定空字串值 (string.Empty)。
  • 如果 id 不是空字串,且字串值的開頭為 EMP,則判定滿足原則 (傳回 true)。 否則,判定原則失敗 (傳回 false)。

Program 檔案中:

  • Microsoft.AspNetCore.ComponentsSystem.Linq 新增命名空間:

    using Microsoft.AspNetCore.Components;
    using System.Linq;
    
  • 新增原則:

    options.AddPolicy("EditUser", policy =>
        policy.RequireAssertion(context =>
        {
            if (context.Resource is RouteData rd)
            {
                var routeValue = rd.RouteValues.TryGetValue("id", out var value);
                var id = Convert.ToString(value, 
                    System.Globalization.CultureInfo.InvariantCulture) ?? string.Empty;
    
                if (!string.IsNullOrEmpty(id))
                {
                    return id.StartsWith("EMP", StringComparison.InvariantCulture);
                }
            }
    
            return false;
        })
    );
    

上述範例是過度簡化的授權原則,這麼做只是為了透過運作範例來示範概念。 如需如何建立和設定授權原則的詳細資訊,請參閱 ASP.NET Core 中的原則型授權

在下列 EditUser 元件中,位於 /users/{id}/edit 的資源具有使用者識別碼 ({id}) 的路由參數。 元件會使用上述 EditUser 授權原則來判斷 id 的路由值是否以 EMP 開頭。 如果 idEMP 開頭,則原則會成功,且會獲得授權而能夠存取元件。 如果 idEMP 以外的值開頭,或如果 id 是空字串,則原則會失敗,且元件不會載入。

EditUser.razor

@page "/users/{id}/edit"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "EditUser")]

<h1>Edit User</h1>

<p>The "EditUser" policy is satisfied! <code>Id</code> starts with 'EMP'.</p>

@code {
    [Parameter]
    public string? Id { get; set; }
}
@page "/users/{id}/edit"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "EditUser")]

<h1>Edit User</h1>

<p>The "EditUser" policy is satisfied! <code>Id</code> starts with 'EMP'.</p>

@code {
    [Parameter]
    public string? Id { get; set; }
}

搭配 Router 元件自訂未經授權的內容

如有下列情況,Router 元件與 AuthorizeRouteView 元件可讓應用程式指定自訂內容:

重要

顯示 <NotAuthorized><NotFound> 內容的 Blazor 路由器功能無法在靜態伺服器端轉譯 (靜態 SSR) 期間運作,因為要求處理完全由 ASP.NET Core 中介軟體管線要求處理,且 Razor 元件針對未經授權的或不正確的要求完全不會轉譯。 使用伺服器端技術來處理靜態 SSR 期間未經授權的和不正確的要求。 如需詳細資訊,請參閱 ASP.NET Core Blazor 轉譯模式

<Router ...>
    <Found ...>
        <AuthorizeRouteView ...>
            <NotAuthorized>
                ...
            </NotAuthorized>
            <Authorizing>
                ...
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
</Router>

AuthorizedNotAuthorized 的內容可以包含任意項目,例如其他互動式元件。

注意

上述內容要求在應用程式的 Program 檔案中註冊串聯驗證狀態服務:

builder.Services.AddCascadingAuthenticationState();
<CascadingAuthenticationState>
    <Router ...>
        <Found ...>
            <AuthorizeRouteView ...>
                <NotAuthorized>
                    ...
                </NotAuthorized>
                <Authorizing>
                    ...
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
    </Router>
</CascadingAuthenticationState>

NotFoundAuthorizedNotAuthorized 的內容可以包含任意項目,例如其他互動式元件。

如果未指定 NotAuthorized 內容,AuthorizeRouteView 會使用下列後援訊息:

Not authorized.

從已啟用驗證的 Blazor WebAssembly 專案範本建立的應用程式會包含位於 Router 元件 <NotAuthorized> 內容中的 RedirectToLogin 元件。 未驗證使用者時 (context.User.Identity?.IsAuthenticated != true),RedirectToLogin 元件會將瀏覽器重新導向至 authentication/login 端點以進行驗證。 使用者會在向識別提供者進行驗證之後,傳回至要求的 URL。

程序性邏輯

如果作為程序性邏輯的一部分,應用程式必須檢查授權規則,請使用 Task<AuthenticationState> 類型的階層式參數來取得使用者的 ClaimsPrincipalTask<AuthenticationState> 可以與其他服務 (例如 IAuthorizationService) 結合來評估原則。

在以下範例中:

  • user.Identity.IsAuthenticated 會針對已驗證 (已登入) 的使用者執行程式碼。
  • user.IsInRole("admin") 會針對 「管理員」角色中的使用者執行程式碼。
  • (await AuthorizationService.AuthorizeAsync(user, "content-editor")).Succeeded 會針對滿足「內容編輯器」原則的使用者執行程式碼。

從專案範本建立時,伺服器端 Blazor 應用程式預設會包含適當的命名空間。 在用戶端端 Blazor 應用程式中,確認元件或應用程式的 _Imports.razor 檔案中是否存在 Microsoft.AspNetCore.AuthorizationMicrosoft.AspNetCore.Components.Authorization 命名空間:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization

ProceduralLogic.razor

@page "/procedural-logic"
@inject IAuthorizationService AuthorizationService

<h1>Procedural Logic Example</h1>

<button @onclick="@DoSomething">Do something important</button>

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? authenticationState { get; set; }

    private async Task DoSomething()
    {
        if (authenticationState is not null)
        {
            var authState = await authenticationState;
            var user = authState?.User;

            if (user is not null)
            {
                if (user.Identity is not null && user.Identity.IsAuthenticated)
                {
                    // ...
                }

                if (user.IsInRole("Admin"))
                {
                    // ...
                }

                if ((await AuthorizationService.AuthorizeAsync(user, "content-editor"))
                    .Succeeded)
                {
                    // ...
                }
            }
        }
    }
}
@page "/procedural-logic"
@inject IAuthorizationService AuthorizationService

<h1>Procedural Logic Example</h1>

<button @onclick="@DoSomething">Do something important</button>

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? authenticationState { get; set; }

    private async Task DoSomething()
    {
        if (authenticationState is not null)
        {
            var authState = await authenticationState;
            var user = authState?.User;

            if (user is not null)
            {
                if (user.Identity is not null && user.Identity.IsAuthenticated)
                {
                    // ...
                }

                if (user.IsInRole("Admin"))
                {
                    // ...
                }

                if ((await AuthorizationService.AuthorizeAsync(user, "content-editor"))
                    .Succeeded)
                {
                    // ...
                }
            }
        }
    }
}

針對錯誤進行疑難排解

常見錯誤:

  • 授權需要 Task<AuthenticationState> 類型的串聯參數。 請考慮使用 CascadingAuthenticationState 來進行提供。

  • 針對 authenticationStateTask 接收到 null

專案很可能不是使用已啟用驗證的伺服器端 Blazor 範本建立。

在 .NET 7 或更早版本中,請將 <CascadingAuthenticationState> 圍繞 UI 樹狀結構的某些部分,例如圍繞 Blazor 路由器:

<CascadingAuthenticationState>
    <Router ...>
        ...
    </Router>
</CascadingAuthenticationState>

在 .NET 8 或更新版本中,請勿使用 CascadingAuthenticationState 元件:

- <CascadingAuthenticationState>
      <Router ...>
          ...
      </Router>
- </CascadingAuthenticationState>

作為替代,請將串聯驗證狀態服務新增至 Program 檔案中的服務集合:

builder.Services.AddCascadingAuthenticationState();

AddCascadingAuthenticationState (.NET 8 或更新版本) 所提供的 CascadingAuthenticationState 元件 (.NET 7 或更早版本) 或服務可支援 AuthenticationState>Task< 串聯參數,使其接收基礎 AuthenticationStateProvider 相依性插入服務。

個人識別資訊 (PII)

當文件涉及個人識別資訊 (PII) 時,Microsoft 是採用 GDPR 對「個人資料」的定義 (GDPR 4.1)

PII 是指已識別或可識別自然人的任何相關資訊。 可識別的自然人是指可透過下列任一項內容,直接或間接識別的人員:

  • 名稱
  • 身分證號碼
  • 位置座標
  • 線上識別碼
  • 其他特定因素
    • 實體
    • 生理
    • 遺傳
    • 精神 (心理)
    • 經濟
    • 文化
    • 社交身分識別

其他資源