學習ASP.NET Core Blazor程式設計系列二十六——登入(5)

2023-02-19 21:00:21
學習ASP.NET Core Blazor程式設計系列一——綜述
學習ASP.NET Core Blazor程式設計系列八——資料校驗
學習ASP.NET Core Blazor程式設計系列十三——路由(完)
學習ASP.NET Core Blazor程式設計系列十五——查詢
學習ASP.NET Core Blazor程式設計系列二十——檔案上傳(完)
 學習ASP.NET Core Blazor程式設計系列二十二——登入(1)
 

九、模擬登入

     登入的本質原理同網頁應用是一樣的,一般的流程是:

      使用者開啟登頁--》輸入賬號密碼後提交表單--》伺服器端驗證成功後生成cookie資訊寫入瀏覽器--》之後使用者存取頁面時瀏覽器會帶上此cookie資訊作為使用者標識--》伺服器端解析此cookie資訊就能識別這個使用者了。

      在webapi出現之後,出現了JWT這樣的認證方式,原理大同小異,相同的是, 認證資訊都是儲存在請求頭中傳遞的,不同是JWT中的認證資訊需要編碼寫入請求頭之後再傳送請求,不像瀏覽器,傳送請求時會自動帶上cookie資訊,不需要編碼。

Blazor中的登入流程可以分成幾下幾個步驟:

  1. 啟用驗證
  2. 製作自定義AuthenticationStateProvider
  3. 修改App.razor
  4. 使用AuthorizeView和進行登入驗證和角色授權

自定義AuthenticationStateProvider

       首先來理解一下什麼是AuthenticationStateProvider。AuthenticationStateProvider是一個抽象類,由Microsoft.AspNetCore.Components.Authorization類庫提供,主要用來提供當前使用者的認證狀態資訊。既然是抽象類,我們需要自定義一個它的子類,由於是模擬登入,進行登入流程的驗證,因此我們先來做一個測試的Provider來試試。

1. 在Visual Studio 2022的解決方案資源管理器中,在專案名稱「BlazorAppDemo」上單擊滑鼠右鍵,在彈出選單中選擇「新增—>新建資料夾」,並將之命名為「Auth」。如下圖。

 

2. 在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中「Auth」資料夾,右鍵單擊,在彈出選單中選擇「新增—>類」,並將類命名為「ImitateAuthStateProvider」。 AuthStateProvider類的最核心方法是 Task<AuthenticationState> GetAuthenticationStateAsync()。基於最簡單的登入機制,我們的擴充套件提供程式如下。具體程式碼如下:

using BlazorAppDemo.Models;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
 
namespace BlazorAppDemo.Auth
{
    public class ImitateAuthStateProvider : AuthenticationStateProvider
    {
        bool isLogin = false;
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            if (isLogin)
            {
 
 
                var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Name,"user"),
                new Claim(ClaimTypes.Role, "admin")
            };
 
                var anonymous = new ClaimsIdentity(claims, "testAuthType");
                return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
            }
            else
            {

            var anonymous = new ClaimsIdentity();
            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));

        }
        }
        public void Login(UserInfo request)
        {
            if (request.UserName == "user" && request.Password == "111111")
                isLogin = true;    
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
           
       
        }

    }

}
ImitateAuthStateProvider繼承AuthenticationStateProvider,並重寫GetAuthenticationStateAsync方法。
  • var anonymous = new ClaimsIdentity();:我們現在進行模擬登入,先做一個匿名的使用者,所以ClaimsIdentity的構造方法中不傳引數。
  • 返回AuthenticationState。
  • 我們給ClaimsIdentity一個List<Claim>屬性,其中有使用者的名字和角色,表示我們已登入成功。

3. 在Visual Studio 2022的解決方案資源管理器中,使用滑鼠雙擊在文字編輯器中開啟Program.cs檔案,使用

builder.Services.AddScoped<ImitateAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(implementationFactory =>
implementationFactory.GetRequiredService<ImitateAuthStateProvider>());

;這二行程式碼使用DI方式注入ImitateAuthStateProvider。具體程式碼如下:

 

using BlazorAppDemo.Data;
using BlazorAppDemo.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Components.Authorization;
using BlazorAppDemo.Auth;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddRazorPages();

builder.Services.AddServerSideBlazor();

builder.Services.AddSingleton<WeatherForecastService>();

System.Console.WriteLine(ConfigHelper.Configuration["ConnectionStrings:BookContext"]);
builder.Services.AddDbContextFactory<BookContext>(opt =>
    opt.UseSqlServer(ConfigHelper.Configuration["ConnectionStrings:BookContext"]));
//builder.Services.AddScoped<AuthenticationStateProvider, ImitateAuthStateProvider>();
builder.Services.AddScoped<ImitateAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(implementationFactory =>
implementationFactory.GetRequiredService<ImitateAuthStateProvider>());
 
 
var app = builder.Build();
 
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");

    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
 
 
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        Console.WriteLine("資料庫開始初始化。");
        var context = services.GetRequiredService<BookContext>();

        // requires using Microsoft.EntityFrameworkCore;
        context.Database.Migrate();
        // Requires using RazorPagesMovie.Models;
        SeedData.Initialize(services);
        Console.WriteLine("資料庫初始化結束。");
    }
 
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();

        logger.LogError(ex, "資料庫資料初始化錯誤.");

    }
}

 
app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

 
app.MapBlazorHub();

app.MapFallbackToPage("/_Host");

app.Run();

 

修改App.razor

現在我們已經完成了登入認證的Provider了,接下來要做的事情,就是讓我們的頁面上的元件,可以獲取登入資訊,來決定是登入使用者是否已經授權。這一步請引數之前的文章學習ASP.NET Core Blazor程式設計系列二十三——登入(3)之中的「七、修改路由與啟動頁面」。

修改Login.razor頁面進行登入

在Visual Studio 2022的文字編輯器中開啟Login.razor元件檔案,我們將滑鼠定位到@code中的SubmitHandler方法 ,寫上我們登入成功的程式碼。具體程式碼如下:  

@page "/Login"
@using BlazorAppDemo.Auth;
@using BlazorAppDemo.Models
@using BlazorAppDemo.Utils
@layout LoginLayout
@inject NavigationManager NavigationManager
@inject ImitateAuthStateProvider AuthStateProvider;
 
<div class="card align-items-center">
    <div class="card-body my-2">
        <h3>Login</h3>
        <hr />
        <EditForm  Model="loginModel" OnValidSubmit="SubmitHandler" OnInvalidSubmit="InvalidHandler">
            <DataAnnotationsValidator />
           
            <div class="form-group">
                <label for="userName">  @HtmlHelper.GetDisplayName(loginModel ,m=> m.UserName)</label>
                <InputText @bind-Value="loginModel.UserName" class="form-control" id="userName" />
                <ValidationMessage For="()=>loginModel.UserName" />

            </div>
            <div class="form-group">
                <label for="pwd"> @HtmlHelper.GetDisplayName(loginModel ,m=> m.Password)</label>
                <InputPassword @bind-Value="loginModel.Password" class="form-control" id="pwd" />
                <ValidationMessage For="()=>loginModel.Password" />
            </div>

            <span class="form-control-plaintext"></span>
            <div class="form-group row">
                <div class="col-sm-10">
            <button class="btn btn-primary">登入</button>
                </div>
            </div>
        </EditForm>

    </div>
</div>

 
@code {
    private UserInfo loginModel = new UserInfo();
    bool isAuthLoading = false;
    private void SubmitHandler()
    {
        Console.WriteLine($"使用者名稱:{loginModel.UserName} ,密碼:{loginModel.Password}");
         isAuthLoading = true;
    try {
         AuthStateProvider.Login(new UserInfo() {
                    UserName = loginModel.UserName,
                    Password =loginModel.Password
        });
            NavigationManager.NavigateTo("/Index");
    } catch (Exception ex) {

         Console.WriteLine(ex.Message);

    } finally {
        isAuthLoading = false;
    }
    }
 
    private void InvalidHandler()
    {

        Console.WriteLine($"使用者名稱: {loginModel.UserName} ,密碼:{loginModel.Password}");

    }

}
 

登入並顯示當前使用者

1.在Visual Studio 2022的文字編輯器中開啟MainLayout.razor元件檔案,在AuthorizeView中顯示當前登入使用者,具體程式碼如下:

2.在Visual Studio 2022的選單欄上,找到「偵錯-->開始偵錯」或是按F5鍵,Visual Studio 2022會生成BlazorAppDemo應用程式,瀏覽器中會Login登入頁面。
@using BlazorAppDemo.Pages
@inherits LayoutComponentBase
 
<PageTitle>BlazorAppDemo</PageTitle>
 
<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>
 
    <main>
        <AuthorizeView>
            <Authorized>
              <div class="top-row px-4">
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
            <p>
       你好, @context.User.Identity.Name!
      </p>
 
        </div> 

        <article class="content px-4">
                    @Body
        </article>
            </Authorized>
            <NotAuthorized>
                <div style="margin: 120px 0; width:100%; text-align: center; color: red;">

                    <span style="font-size:20px">檢測到登入超時,請重新<a href="/login" style="text-decoration:underline">登入</a>

</span> </div> <RedirectToLogin></RedirectToLogin> </NotAuthorized> </AuthorizeView> </main> </div> 

3.我們在使用者名稱輸入框中輸入使用者名稱,在密碼輸入框中輸入密碼,點選「登入」按鈕,進行模擬登入。我們進入了系統。如下圖。

 

     到此為止,我們已經實現了Blazor的登入認證流程,接下來我們要來實現通過jwt進行登入。