iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
0
Modern Web

今晚,我想來點Blazor系列 第 23

Day 23:Blazor jwt登入範例(2) -- 了解實作驗證流程

  • 分享至 

  • xImage
  •  

在開始製作jwt token之前,我們先來了解在Blazor WebAssembly中,是怎麼實作Authentication和Authorization的。

整個流程可分成下面幾個步驟:

  1. 啟用驗證
  2. 實作custom AuthenticationStateProvider
  3. 修改Router.razor
  4. 使用AuthorizeView和進行驗證和角色授權

啟用Client-side Authentication

一開始我們要先到nuget安裝一個套件:
Microsoft.AspNetCore.Components.WebAssembly.Authentication
安裝完之後,到Program.cs加入下方這一行,將authorization Service加到IServiceCollection中。:

builder.Services.AddAuthorizationCore();

實作custom AuthenticationStateProvider

先來理解什麼是AuthenticationStateProvider。AuthenticationStateProvider是一個抽象類別,主要用來提供登入使用者的Claim資訊,所以我們要做jwt驗證的話,需要自訂一個類別來繼承AuthenticationStateProvider。

由於今天這篇是先介紹驗證流程,因此我們先用一個測試的Provider來試試,現在新增一個TestAuthStateProvider:

public class TestAuthStateProvider : AuthenticationStateProvider
    {
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {          
            var anonymous = new ClaimsIdentity();
           
            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
        }
    }
  • TestAuthStateProvider繼承AuthenticationStateProvider,並覆寫GetAuthenticationStateAsync方法。
  • var anonymous = new ClaimsIdentity();:這邊先做一個匿名的使用者,所以ClaimsIdentity的建構式暫時不給參數。
  • 回傳AuthenticationState物件

完成TestAuthStateProvider後,在Program.cs用DI方式註冊TestAuthStateProvider:

builder.Services.AddScoped<AuthenticationStateProvider, TestAuthStateProvider>();

修改Router.razor

現在我們已經有驗證的Provider了,接下來要做的事情,就是讓我們頁面上的元件,可以取得user資訊,來決定是否顯示。

由於user資訊是要讓全站的元件都可以取得的,要怎麼做呢? 還記得Cascading Parameter嗎? 它可以讓底下所有的元件都能存取到資料,因此我們在Router.razor中,使用CascadingAuthenticationState來包住Router元件。CascadingAuthenticationState會從TestAuthStateProvider的GetAuthenticationStateAsync方法取得user資訊:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <h3>You shall not pass!</h3>
                    <img src="https://media.giphy.com/media/8abAbOrQ9rvLG/giphy.gif" class="img-fluid" />
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>

            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
  • Router.razor中的AuthorizeRouteView,它結合了RouteView和AuthorizeView的作用,意味著有找到符合路徑的路由,且通過授權的使用者才會顯示
  • 如果未通過授權,可以像第5行那樣,用NotAuthorized元件來顯示未授權之類的文字

要使用CascadingAuthenticationState的話,需要加入Microsoft.AspNetCore.Components.Authorization命名空間,或是在_Imports.razor中加入

@using Microsoft.AspNetCore.Components.Authorization

使用AuthorizeView和進行驗證和角色授權

還記得我們在樣板元件2最後做的超級英雄清單嗎? 現在只有通過驗證授權的人才能看到這些超級英雄的機密資料XD,因此現在我們來加上驗證吧

<div class="row">
    <div class="col-8">
        <AuthorizeView>
            <Authorized>
                <SuperHeroes />
            </Authorized>
            <NotAuthorized>
                <h3>You shall not pass!</h3>
                <img src="https://media.giphy.com/media/8abAbOrQ9rvLG/giphy.gif" class="img-fluid" />
            </NotAuthorized>
        </AuthorizeView>
    </div>
</div>

使用AuthorizeView元件來顯示授權與未授權的內容,由於我們目前是匿名使用者,所以是未通過的,這時候就會顯示NotAuthorized中的gif:

模擬登入成功

如果想要看到內容就必須要登入成功。現在我們可以回到TestAuthStateProvider,將GetAuthenticationStateAsync的程式碼改成下面這樣:

public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Name,"Nick Fury"),
                new Claim(ClaimTypes.Role, "admin")
            };
          
            var anonymous = new ClaimsIdentity(claims, "testAuthType");
            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
        }
        ```
現在我們給ClaimsIdentity一個List`<Claim>`物件,其中有使用者的名字還有角色,表示我們已登入成功。並在超級英雄清單頁面顯示user的名字:

角色授權

在AuthorizeView標籤,可以使用角色來進行授權:

<AuthorizeView Roles="admin">
            <Authorized>
                <h3>Welcome, @context.User.Identity.Name</h3>
                <br />
                <SuperHeroes />
            </Authorized>
            <NotAuthorized>
                <h3>You shall not pass!</h3>
                <img src="https://media.giphy.com/media/8abAbOrQ9rvLG/giphy.gif" class="img-fluid" />
            </NotAuthorized>
        </AuthorizeView>

在元件使用 [Authorize] attribute

當我們針對特定頁面進行驗證授權時,可以在該頁面設定[Authorize],比方說我現在只讓成功登入的人看到Counter頁,就可以在Counter.razor加上[Authorize]

@page "/counter"
@attribute [Authorize]

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

當然也可以針對角色開放,只要在[Authorize]設定角色即可:

@attribute [Authorize(Roles = "CounterAdmin")]

因為Counter是設定CounterAdmin才能看,所以admin的Nick Fury就無法進入該頁了

另外,官方文件有提到[Authorize]只對@page元件有作用,如果套用在一般元件,是不會做認證授權的,還有如果要顯示頁面的特定區塊,可以使用AuthorizeView替代。

OK,以上就是Blazor的驗證授權流程,明天我們就來實作jwt的部分吧


上一篇
Day 22:Blazor jwt登入範例(1) -- 製作登入表單
下一篇
Day 24:Blazor jwt登入範例(3) -- 產生jwt token
系列文
今晚,我想來點Blazor30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言