在開始製作jwt token之前,我們先來了解在Blazor WebAssembly中,是怎麼實作Authentication和Authorization的。
整個流程可分成下面幾個步驟:
一開始我們要先到nuget安裝一個套件:
Microsoft.AspNetCore.Components.WebAssembly.Authentication
安裝完之後,到Program.cs加入下方這一行,將authorization Service加到IServiceCollection中。:
builder.Services.AddAuthorizationCore();
先來理解什麼是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後,在Program.cs用DI方式註冊TestAuthStateProvider:
builder.Services.AddScoped<AuthenticationStateProvider, TestAuthStateProvider>();
現在我們已經有驗證的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>
要使用CascadingAuthenticationState的話,需要加入Microsoft.AspNetCore.Components.Authorization命名空間,或是在_Imports.razor中加入
@using Microsoft.AspNetCore.Components.Authorization
還記得我們在樣板元件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],比方說我現在只讓成功登入的人看到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的部分吧