Resource-based authorization in Blazor

關聯文章

NET8 Blazor Web App 進階授權檢查與問題(BUG)

參考文章

授權檢查方法

已知授權檢查 Authorize 方法有 Role-based, Policy-based 最近新發現 Resource-based 授權檢查。

Role-based 直接與角色綁定。Policy-based 有小程度的動態,比如:18歲檢查這種以登入用戶個資來判斷。Resource-based 可以處理更動態的情境。

Resource-based authorization 關鍵知識

因為 Role-based 與 Policy-based 都無法滿足才使用 Resource-based 授權檢查使用命令式授權

可注入 IAuthorizationService 介面服務,使用 AuthorizeAsync 函式來觸發 Policy 或 Requirements 驗證。是的它的核心與 policy-based 都是一樣的,不過多了名為 Resource 的參數。這個 Resrouce 參數可以在執行期動態指定,role-based 與 policy-based 都無法在執行期動態指定 Resource。

案例:AuthorizePage in Blazor

Blazor Page Componnet

FooPage.razor
@page "/DEMO006"
@attribute [Page("DEMO006", "展示功能頁面")]

@* 取代 @attribute [Authorize("AuthPage")]。因為 F5 刷新會 => 404! *@
<AuthorizePage Page=@this />

<MudContainer>
  ...content...
</MudContainer>

@code {
  ...略...
}

AuthorizePage.razor

Shared\AuthorizePage.razor
@using AsvtTPL.Components.Account.Shared
@inject NavigationManager navSvc
@inject IAuthorizationService authSvc

@*
* ref→[Resource-based authorization in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-8.0)
*
* 取代 @attribute [Authorize("AuthPage")]。
* 因為 F5 刷新會 => 404!
* 注意:必需搭配 Vista.Biz.AOP.PageAttribute 宣示。例:
* ```
* @attribute [Page("DEMO013", "步步申請樣板")]
* ```
*@

@* 授權檢查中 *@
@if (authing)
{
  <MudProgressLinear Color=Color.Error Indeterminate />
}

@code {
  [CascadingParameter] Task<AuthenticationState> AuthStateTask { get; set; } = default!;

  /// <summary>
  /// 必需一開始就指定。資源可在動態時期指定。
  /// </summary>
  [Parameter, EditorRequired] public ComponentBase Page { get; set; } = default!;

  //## State
  bool authing = true;

  protected override async Task OnParametersSetAsync()
  {
    Type pageType = Page.GetType();

    //# RedirectToLogin when not IsAuthenticated
    var authState = await AuthStateTask;
    if (!(authState.User.Identity?.IsAuthenticated ?? false))
    {
      /// 401 未登入
      navSvc.NavigateTo($"/login?returnUrl={Uri.EscapeDataString(navSvc.Uri)}", forceLoad: true);
      return;
    }

    //※ 用 AuthorizeAsync 觸發 AuthPageRequirement 檢驗,帶入資源:pageType。
    var result = await authSvc.AuthorizeAsync(authState.User, pageType, new AuthPageRequirement());
    if (!result.Succeeded)
    {
      /// 403 授權不足 禁止使用此功能
      navSvc.NavigateTo($"/ErrorStatus/{403}", forceLoad: false);
      return;
    }

    // Sucess
    authing = false;
  }
}

AuthPageRequirement.cs

AuthPageRequirement.cs
using Microsoft.AspNetCore.Authorization;
using System.Reflection;
using Vista.Biz.AOP;
using Vista.Models;

namespace Vista.Services;

// 宣告 Authorization Requirement
// ※ 不必實作內容,只做為需求的宣示。
class AuthPageRequirement : IAuthorizationRequirement { }

// 實作 handler --- Authorization Requirement 檢驗程序
internal class AuthPageHandler(
  ILogger<AuthPageHandler> _logger,
  AccountService _accSvc)
  : AuthorizationHandler<AuthPageRequirement>
{
  // 檢驗程序實作
  protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthPageRequirement requirement)
  {
    //# 未登入離開。
    if (!context.User.Identity?.IsAuthenticated ?? false)
      return Task.CompletedTask;

    //# 取得需要資源,若非預期資源離開。
    Type? pageType = context.Resource as Type;
    if (pageType == null) return Task.CompletedTask;

    //# 取得客製資源
    // AuthPage 屬性 := @attribute[Page("DEMO013", "步步申請樣板")]
    PageAttribute? pageAttr = pageType.GetCustomAttribute<PageAttribute>();
    if (pageAttr == null) return Task.CompletedTask;
    // 功能代碼
    string funcCode = pageAttr.FuncId;

    //# 開始驗證
    // 是否登入者有授權的功能清單。
    //if (!context.User.IsInRole(funcCode)) return Task.CompletedTask;
    var authUser = _accSvc.GetAuthDataFromPool(context.User.Identity!.Name!);
    if (authUser is null) return Task.CompletedTask;
    if (!authUser.AuthFuncList().Contains(funcCode)) return Task.CompletedTask;

    // OK
    context.Succeed(requirement); // 滿足此項授權需求
    _logger.LogInformation($"User [{context.User.Identity!.Name}] has checked AuthPageRequirement successfully.");
    return Task.CompletedTask;
  }
}

(EOF)

Last updated