本來今天要介紹的是驗證授權,但官方那一套我不是很喜歡,所以就從網路上找其他的解決辦法,果不其然就找到了一套,依照Blazor WebAssembly - JWT Authentication Example & Tutorial的思路,簡單的做了一套 JWT 驗證流程,讓我們開始吧!
若要保留登入資料供下次使用,我們需要透過 localStorage
來達成,所以如果你的需求不是如此,便可以省略這一步。
我們先建立一個服務與其介面來處理 localStorage
相關方法:
1 2 3 4 5 6 7 8 9
| public interface ILocalStorageService { Task<T> GetItem<T>(string key);
Task SetItem<T>(string key, T value);
Task RemoveItem(string key); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class LocalStorageService : ILocalStorageService { private readonly IJSRuntime _jsRuntime;
public LocalStorageService(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; }
public async Task<T> GetItem<T>(string key) { var json = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
if (json == null) return default;
return JsonSerializer.Deserialize<T>(json); }
public async Task SetItem<T>(string key, T value) { await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, JsonSerializer.Serialize(value)); }
public async Task RemoveItem(string key) { await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key); } }
|
因為 windows
物件本身就包含 localStorage
的處理,所以不需要特別撰寫 javascript
。
然後建立驗證服務:
1 2 3 4 5
| public interface IAuthService { Task<bool> Check(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class AuthService : IAuthService { private readonly HttpClient _httpClient; private readonly ILocalStorageService _localStorageService;
public AuthService( HttpClient httpClient, ILocalStorageService localStorageService ) { _httpClient = httpClient; _localStorageService = localStorageService; }
public async Task<bool> Check() { var request = new HttpRequestMessage(HttpMethod.Get, "/auth");
var token = await _localStorageService.GetItem<string>("token");
if (token is null) { return false; }
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using var response = await _httpClient.SendAsync(request);
if (response.StatusCode == HttpStatusCode.Unauthorized) { return false; }
return true; } }
|
執行此驗證方法,會從 localStorage
取得 token
並添加到 header
中然後透過 HTTP 方法 get
呼叫 /auth
的 api,若 localStorage
中找不到 token
或者 HTTP 回應的狀態碼為 401
則回傳 false
,反之為 true
。
但此時並沒有串接任何一個 API,所以需要建立一個假的 HttpClientHandler
來做測試,透過繼承 HttpClientHandler
,並覆寫其 SendAsync
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class FakeHttpClientHandler : HttpClientHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.RequestUri.AbsolutePath == "/auth" && request.Method == HttpMethod.Get) { return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; } else { return await base.SendAsync(request, cancellationToken); } } }
|
這樣一來只要呼叫 HttpClient.SendAsync
時 HTTP 方法為 get
且位置為 /auth
,就會無條件返回成功。
最後記得將上面的服務註冊到 DI Container 如:
1 2 3
| builder.Services.AddScoped(sp => new HttpClient(new FakeHttpClientHandler()) { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }) .AddScoped<ILocalStorageService, LocalStorageService>() .AddScoped<IAuthService, AuthService>();
|
基本的驗證流程就完成了,只要有元件呼叫 IAuthService.Check
方法,就會檢查 token
是否存在且有效。
這邊提供測試用的 Component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @page "/day26sample" @inject ILocalStorageService LocalStorageService; <h3>Day26Sample</h3> <button @onclick="SetUser">Set Token</button> <button @onclick="RemoveUser">Remove Token</button> <NavLink href="/day26authsample">Go Auth</NavLink> @code {
}
@functions{ private async Task SetUser() { await LocalStorageService.SetItem<string>("token", @"blazor30days"); }
private async Task RemoveUser() { await LocalStorageService.RemoveItem("token"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @page "/day26authsample" @inject IAuthService AuthService @inject NavigationManager Navigation <h3>Day26AuthSample</h3> <NavLink href="/day26sample">Go Back</NavLink>
@code { protected override async Task OnInitializedAsync() { if (!await AuthService.Check()) { Navigation.NavigateTo("/day26sample"); }
await base.OnInitializedAsync(); } }
|
可以試試看沒有設定 token 的情況下,是無法瀏覽 /day26authsample
頁面的,並會在 OnInitializedAsync
後導向 /day26sample
頁面,若有大量頁面需要做判斷,也可以考慮抽成一個共用驗證元件,透過繼承的方式一次修改所有需要驗證的元件。
以上就是我對於 JWT 驗證授權的處理方式,本來看到官方的範例想跳過此內容的,但又覺得介紹 blazor 不介紹驗證授權相關的技巧說不過去,所以就衍生出了這一篇,希望能幫助到大家。完整程式碼在範本程式碼 - Day26。
感謝大家的閱讀,我們明天見。
參考資料
Blazor WebAssembly - JWT Authentication Example & Tutorial