使用WebApi和Asp.Net Core Identity 認證 Blazor WebAssembly(Blazor客戶端應用)


原文:https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-webapi-aspnet-core-identity/

由於Blazor框架已經有所更新,翻譯中有些內容我根據實際情況做了更改。

設置:創建解決方案

選擇Blazor應用

項目名稱

選擇Blazor WebAssembly App(這里要勾選Asp.Net Core Host),如果找不到Blazor WebAssembly App,請先在命令行執行以下命令:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview1.19508.20

解決方案創建之后,我們將開始對AuthenticationWithBlazorWebAssembly.Server這個項目進行一些更改。

配置WebAPI

在配置WebAPI之前我先安裝一些NuGet包:

    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Blazor.Server" Version="3.0.0-preview9.19465.2" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0">
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />

設置Identity數據庫:連接字符串

在進行任何設置之前,數據庫方面需要一個連接字符串。這通常是保存在appsettings.json中的,但Blazor托管模版並未提供此文件,所以我們需要手動添加此文件。

在AuthenticationWithBlazorWebAssembly.Server項目右鍵添加 -> 新建項,然后選擇應用設置文件

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=AuthenticationWithBlazorWebAssembly;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

該文件帶有一個已經設置好的連接字符串,你可以隨時將其指向需要的地方。我們只需要添加一個數據庫名就可以了,其余的保持默認值。

設置Identity數據庫:DbContext

在AuthenticationWithBlazorWebAssembly.Server項目跟目錄創建一個名為Data的目錄,然后使用下面代碼添加一個名為ApplicationDbContext的類文件。

    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions options) : base(options) {
        }
    }

 因為我們使用Identity需要將信息存儲在數據庫中,所以我們不是從DbContext繼承,而是從IdentityDbContext繼承。IdentityDbContext基類包含EF配置管理Identity數據庫表需要的所有配置。

設置Identity數據庫:注冊服務

Startup類中,我們需要添加一個構造函數,接收IConfiguration參數和一個屬性來存儲它。IConfiguration允許我們訪問appsettings.json文件,如:連接字符串。

 

        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

接下來我們將以下代碼添加到ConfigureServices方法的頂部。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddDefaultIdentity<IdentityUser>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

      //這里省略掉其他代碼
        }

這里兩行代碼將ApplicationDbContext添加到服務集合中。然后為ASP.NET Core Identity注冊各種服務並通過ApplicationDbContext使用Entity Framework作為數據存儲。

設置Identity數據庫:創建數據庫

現在可以為數據庫創建初始遷移。在程序包管理器控制台運行以下命令。

Add-Migration CreateIdentitySchema -o Data/Migations

命令運行完成,你應該能在DataMigrations文件夾中看到遷移文件。在控制台中運行命令Update-Database將遷移應用到數據庫。

在運行遷移命令時遇到任何問題,請確保在程序包管理器中選擇AuthenticationWithBlazorWebAssembly.Server項目作為默認項目。

啟用身份驗證:注冊服務

接下來在API中啟用身份驗證。同樣,在ConfigureServices中,在上一節添加的代碼之后添加以下代碼。

public void ConfigureServices(IServiceCollection services)
{
  //這里省略到其他代碼
  services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = Configuration["JwtIssuer"],
                        ValidAudience = Configuration["JwtAudience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
                    };
                });
  //這里省略掉其他代碼
}

上面代碼想服務容器添加和設置一些身份驗證所需的服務。然后為JSON Web Tokens(JWT)添加處理程序,並配置接收到的JWTs應該如何驗證。你可以根據需求調整這些設置。

啟用身份驗證:應用程序設置

有一些設置要從appsettings.json文件中加載。

  • Configuration["JwtIssuer"]
  • Configuration["JwtAudience"]
  • Configuration["JwtSecurityKey"]

我們還未將它們添加到appsettings文件中。現在添加它們並添加一個設置用來控制令牌的持續時間,稍后我們會使用這個設置。

"JwtSecurityKey": "RANDOM_KEY_MUST_NOT_BE_SHARED",
"JwtIssuer": "https://localhost",
"JwtAudience": "https://localhost",
"JwtExpiryInDays": 1,

保證JwtSecurityKey 的安全是非常重要的,因為這是用來對API產生的令牌簽名的,如果泄露那么你的應用程序將不在安全。

由於我們在本地運行所有內容,所以我將IssuerAudience設置為localhost。如果在生產環境使用它,我們需要將Issuer 設置為API運行的域名,將Audience設置為客戶端應用程序運行的域名。

啟用身份驗證:添加中間件

最后,我們需要在Configure 方法中將必要的中間件添加到管道中。這將在API中啟用身份驗證和授權功能。將以下代碼添加到app.UseEndpoints中間件前面。

app.UseAuthentication();
app.UseAuthorization();

這就是Startup類所需要的所有東西。現在API已經啟用了身份驗證。

你可以通過向WeatherForecastController中的Get方法添加[Authorize]屬性來測試一切是否正常。然后啟用應用程序並導航到Fetch Data頁面,應該不會加載任何數據,應該會在控制台中看到401錯誤。

添加賬戶(account)控制器

為了讓人們登錄到我們的應用程序,他們需要能夠注冊。我們將添加一個帳戶控制器,它將負責創建新帳戶。

    [Route("api/[controller]")]
    [ApiController]
    public class AccountsController : ControllerBase
    {
        //private static UserModel LoggedOutUser = new UserModel { IsAuthenticated = false };

        private readonly UserManager<IdentityUser> _userManager;

        public AccountsController(UserManager<IdentityUser> userManager)
        {
            _userManager = userManager;
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody]RegisterModel model)
        {
            var newUser = new IdentityUser { UserName = model.Email, Email = model.Email };

            var result = await _userManager.CreateAsync(newUser, model.Password);

            if (!result.Succeeded)
            {
                var errors = result.Errors.Select(x => x.Description);

                return BadRequest(new RegisterResult { Successful = false, Errors = errors });

            }

            return Ok(new RegisterResult { Successful = true });
        }
    }

 

Post操作使用ASP.NET Core Identity從RegisterModel來創建系統的新用戶。

我們還沒用添加注冊模型,現在使用以下代碼添加到AuthenticationWithBlazorWebAssembly.Shared項目中,稍后我們的Blazor應用程序將會使用到它。

    public class RegisterModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

如果一切順利,則會返回一個成功的RegisterResult,否則會返回一個失敗的RegisterResult,我們一樣將它添加到AuthenticationWithBlazorWebAssembly.Shared項目中。

    public class RegisterResult
    {
        public bool Successful { get; set; }
        public IEnumerable<string> Errors { get; set; }
    }

添加登錄(login)控制器

現在我們有了用戶注冊的方式,我們還需要用戶登錄方式。

 [Route("api/[controller]")]
    [ApiController]
    public class LoginController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        private readonly SignInManager<IdentityUser> _signInManager;

        public LoginController(IConfiguration configuration,
            SignInManager<IdentityUser> signInManager)
        {
            _configuration = configuration;
            _signInManager = signInManager;
        }

        [HttpPost]
        public async Task<IActionResult> Login([FromBody] LoginModel login)
        {
            var result = await _signInManager.PasswordSignInAsync(login.Email, login.Password, false, false);

            if (!result.Succeeded) return BadRequest(new LoginResult { Successful = false, Error = "Username and password are invalid." });

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, login.Email)
            };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expiry = DateTime.Now.AddDays(Convert.ToInt32(_configuration["JwtExpiryInDays"]));

            var token = new JwtSecurityToken(
                _configuration["JwtIssuer"],
                _configuration["JwtAudience"],
                claims,
                expires: expiry,
                signingCredentials: creds
            );

            return Ok(new LoginResult { Successful = true, Token = new JwtSecurityTokenHandler().WriteToken(token) });
        }
    }

登錄控制器(login controller)使用ASP.NET Core Identity SignInManger驗證用戶名和密碼。如果它們都正確,則生成一個新的JSON Web Token並在LoginResult返回給客戶端。

像之前一樣,我們需要將LoginModelLoginResult添加到AuthenticationWithBlazorWebAssembly.Shared項目中。

    public class LoginModel
    {
        [Required]
        public string Email { get; set; }

        [Required]
        public string Password { get; set; }

        public bool RememberMe { get; set; }
    }
    public class LoginResult
    {
        public bool Successful { get; set; }
        public string Error { get; set; }
        public string Token { get; set; }
    }

這就是API需要的所有東西。我們現在已經將其配置為通過JSON web tokens進行身份驗證。接下來我們需要為Blazor WebAssembly(客戶端)應用程序添加注冊新用戶和登錄控制器。

配置Blazor客戶端

接下來我們關注Blazor。首先需要安裝Blazored.LocalStorage,我們稍后將需要它在登錄時從API中持久化驗證令牌。

我們還需要在App組件中使用AuthorizeRouteView組件替換RouteView組件(這里需要使用Microsoft.AspNetCore.Components.Authorization NuGet包並在_Imports.razor添加@using Microsoft.AspNetCore.Components.Authorization)。

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

此組件提供類型為Task<AuthenticationState>的級聯參數。AuthorizeView通過使用它來確定當前用戶的身份驗證狀態。

但是任何組件都可以請求參數並使用它來執行過程邏輯,例如:

@page "/"

<button @onclick="@LogUsername">Log username</button>

@code {
    [CascadingParameter]
    private Task<AuthenticationState> authenticationStateTask { get; set; }

    private async Task LogUsername()
    {
        var authState = await authenticationStateTask;
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            Console.WriteLine($"{user.Identity.Name} is authenticated.");
        }
        else
        {
            Console.WriteLine("The user is NOT authenticated.");
        }
    }
}

創建自定義AuthenticationStateProvider

因為我們使用Blazor WebAssembly,所以我們需要為AuthenticationStateProvider提供自定義實現。因為在客戶端應用程序有太多的選項,所以無法設計一個適用於所有人的默認類。

我們需要重寫GetAuthenticationStateAsync方法。在此方法中,我們需要確定當前用戶是否經過身份驗證。我們還將添加兩個輔助方法,當用戶登錄或注銷時,我們將使用這些方法更新身份驗證狀態。

public class ApiAuthenticationStateProvider : AuthenticationStateProvider
    {
        private readonly HttpClient _httpClient;
        private readonly ILocalStorageService _localStorage;

        public ApiAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
        {
            _httpClient = httpClient;
            _localStorage = localStorage;
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var savedToken = await _localStorage.GetItemAsync<string>("authToken");

            if (string.IsNullOrWhiteSpace(savedToken))
            {
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            }

            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", savedToken);

            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(savedToken), "jwt")));
        }

        public void MarkUserAsAuthenticated(string token)
        {
            var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt"));
            var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
            NotifyAuthenticationStateChanged(authState);
        }

        public void MarkUserAsLoggedOut()
        {
            var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
            var authState = Task.FromResult(new AuthenticationState(anonymousUser));
            NotifyAuthenticationStateChanged(authState);
        }

        private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
        {
            var claims = new List<Claim>();
            var payload = jwt.Split('.')[1];
            var jsonBytes = ParseBase64WithoutPadding(payload);
            var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);

            keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);

            if (roles != null)
            {
                if (roles.ToString().Trim().StartsWith("["))
                {
                    var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());

                    foreach (var parsedRole in parsedRoles)
                    {
                        claims.Add(new Claim(ClaimTypes.Role, parsedRole));
                    }
                }
                else
                {
                    claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
                }

                keyValuePairs.Remove(ClaimTypes.Role);
            }

            claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));

            return claims;
        }

        private byte[] ParseBase64WithoutPadding(string base64)
        {
            switch (base64.Length % 4)
            {
                case 2: base64 += "=="; break;
                case 3: base64 += "="; break;
            }
            return Convert.FromBase64String(base64);
        }
    }

這里有很多代碼,讓我們一步一步來分析。

CascadingAuthenticationState組件調用GetAuthenticationStateAsync方法來確定當前用戶是否經過驗證。

上面的代碼,我們檢查local storge是否有驗證令牌。如果local storge中沒有令牌,那么我們將返回一個新的AuthenticationState,其中包含一個空的ClaimsPrincipal。這就說明當前用戶用戶沒有經過身份驗證。

如果有令牌,讀取並設置HttpClient的默認Authorization Header,並返回一個包含ClaimsPrincipal新的AuthenticationState的令牌聲明。該聲明(Claims)使用ParseClaimsFromJwt方法從令牌中提取。此方法解碼令牌並返回其中包含的聲明。

MarkUserAsAuthenticated輔助方法用於登錄時調用NotifyAuthenticationStateChanged方法,該方法觸發AuthenticationStateChanged事件。這將通過CascadingAuthenticationState組件級聯新的身份驗證狀態。

MarkUserAsLoggedOut用於用戶注銷時。

Auth Service

Auth Service將在組件中注冊用戶並登錄到應用程序和用戶注銷使用。

public class AuthService : IAuthService
    {
        private readonly HttpClient _httpClient;
        private readonly AuthenticationStateProvider _authenticationStateProvider;
        private readonly ILocalStorageService _localStorage;

        public AuthService(HttpClient httpClient,
            AuthenticationStateProvider authenticationStateProvider,
            ILocalStorageService localStorage)
        {
            _httpClient = httpClient;
            _authenticationStateProvider = authenticationStateProvider;
            _localStorage = localStorage;
        }

        public async Task<RegisterResult> Register(RegisterModel registerModel)
        {
            var result = await _httpClient.PostJsonAsync<RegisterResult>("api/accounts", registerModel);

            return result;
        }

        public async Task<LoginResult> Login(LoginModel loginModel)
        {
            var loginAsJson = JsonSerializer.Serialize(loginModel);
            var response = await _httpClient.PostAsync("api/Login", new StringContent(loginAsJson, Encoding.UTF8, "application/json"));
            var loginResult = JsonSerializer.Deserialize<LoginResult>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            if (!response.IsSuccessStatusCode)
            {
                return loginResult;
            }

            await _localStorage.SetItemAsync("authToken", loginResult.Token);
            ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsAuthenticated(loginResult.Token);
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", loginResult.Token);

            return loginResult;
        }

        public async Task Logout()
        {
            await _localStorage.RemoveItemAsync("authToken");
            ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsLoggedOut();
            _httpClient.DefaultRequestHeaders.Authorization = null;
        }
    }

Register方法提交registerModel給accounts controller並返回RegisterResult給調用者。

Login 方法類似於Register 方法,它將LoginModel 發送給login controller。但是,當返回一個成功的結果時,它將返回一個授權令牌並持久化到local storge。

最后我們調用ApiAuthenticationStateProvider上的方法MarkUserAsAuthenticated ,設置HttpClient的默認authorization header。

Logout 這個方法就是執行與Login 方法相反的操作。

注冊組件(Register Component)

我們已經到了最后階段了。現在我們可以將注意力轉向UI,並創建一個允許人們在站點注冊的組件。

@page "/register"
@inject IAuthService AuthService
@inject NavigationManager NavigationManager

<h1>Register</h1>

@if (ShowErrors) {
    <div class="alert alert-danger" role="alert">
        @foreach (var error in Errors) {
            <p>@error</p>
        }
    </div>
}

<div class="card">
    <div class="card-body">
        <h5 class="card-title">Please enter your details</h5>
        <EditForm Model="RegisterModel" OnValidSubmit="HandleRegistration">
            <DataAnnotationsValidator />
            <ValidationSummary />

            <div class="form-group">
                <label for="email">Email address</label>
                <InputText Id="email" class="form-control" @bind-Value="RegisterModel.Email" />
                <ValidationMessage For="@(() => RegisterModel.Email)" />
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <InputText Id="password" type="password" class="form-control" @bind-Value="RegisterModel.Password" />
                <ValidationMessage For="@(() => RegisterModel.Password)" />
            </div>
            <div class="form-group">
                <label for="confirmpassword">Confirm Password</label>
                <InputText Id="confirmpassword" type="password" class="form-control" @bind-Value="RegisterModel.ConfirmPassword" />
                <ValidationMessage For="@(() => RegisterModel.ConfirmPassword)" />
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </EditForm>
    </div>
</div>

@code {

    private RegisterModel RegisterModel = new RegisterModel();
    private bool ShowErrors;
    private IEnumerable<string> Errors;

    private async Task HandleRegistration() {
        ShowErrors = false;

        var result = await AuthService.Register(RegisterModel);

        if (result.Successful) {
            NavigationManager.NavigateTo("/login");
        } else {
            Errors = result.Errors;
            ShowErrors = true;
        }
    }

}

注冊組件包含一個表單讓用戶輸入他們的電子郵件和密碼。提交表單時,會調用AuthService 的方法Register 。如果注冊成功那么用戶會被導航到登錄頁,否則,會將錯誤顯示給用戶。

登錄組件(Login Component)

現在我們可以注冊一個新的帳戶,我們需要能夠登錄。登錄組件將用於此。

@page "/login"
@inject IAuthService AuthService
@inject NavigationManager NavigationManager

<h1>Login</h1>

@if (ShowErrors) {
    <div class="alert alert-danger" role="alert">
        <p>@Error</p>
    </div>
}

<div class="card">
    <div class="card-body">
        <h5 class="card-title">Please enter your details</h5>
        <EditForm Model="loginModel" OnValidSubmit="HandleLogin">
            <DataAnnotationsValidator />
            <ValidationSummary />

            <div class="form-group">
                <label for="email">Email address</label>
                <InputText Id="email" Class="form-control" @bind-Value="loginModel.Email" />
                <ValidationMessage For="@(() => loginModel.Email)" />
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <InputText Id="password" type="password" Class="form-control" @bind-Value="loginModel.Password" />
                <ValidationMessage For="@(() => loginModel.Password)" />
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </EditForm>
    </div>
</div>

@code {

    private LoginModel loginModel = new LoginModel();
    private bool ShowErrors;
    private string Error = "";

    private async Task HandleLogin() {
        ShowErrors = false;

        var result = await AuthService.Login(loginModel);

        if (result.Successful) {
            NavigationManager.NavigateTo("/");
        } else {
            Error = result.Error;
            ShowErrors = true;
        }
    }

}

與注冊組件類似的設計,我們也提供一個表單用於用戶輸入電子郵件和密碼。表單提交時,將調用AuthService的方法Login。如果登錄成功,用戶將被重定向到主頁,否則將顯示錯誤消息。

注銷組件(Logout Component)

我們現在可以注冊和登錄,但我們也需要注銷的功能。我用了一個頁面組件來做這個,但是你也可以通過點擊某個地方的按鈕來實現。

@page "/logout"
@inject IAuthService AuthService
@inject NavigationManager NavigationManager


@code {

    protected override async Task OnInitializedAsync() {
        await AuthService.Logout();
        NavigationManager.NavigateTo("/");
    }

}

這個組件沒有用戶界面,當用戶導航到它時,將調用AuthService上的方法Logout,然后將用戶重定向回主頁。

添加一個LoginDisplay組件並更新MainLayout組件

最后的任務是添加一個LoginDisplay組件並更新MainLayout 組件。

LoginDisplay 組件與Blazor Server模版一樣,如果未經驗證,它將顯示登錄與注冊鏈接,否則顯示電子郵件和注銷鏈接。

<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity.Name!
        <a href="/logout">Log out</a>
    </Authorized>
    <NotAuthorized>
        <a href="/register">Register</a>
        <a href="/login">Log in</a>
    </NotAuthorized>
</AuthorizeView>

我們現在只需要更新MainLayout組件。

@inherits LayoutComponentBase

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        <LoginDisplay />
        <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
    </div>

    <div class="content px-4">
        @Body
    </div>
</div>

注冊服務(Registering Services)

最后在Startup類中注冊服務。

            services.AddBlazoredLocalStorage();
            services.AddAuthorizationCore();
            services.AddScoped<AuthenticationStateProvider, ApiAuthenticationStateProvider>();
            services.AddScoped<IAuthService, AuthService>();

如果一切都按計划進行,那么你應該得到這樣的結果。

總結

這篇文章展示了如何WebAPI和ASP.NET Core Identity創建一個帶有身份驗證的Blazor WebAssembly(Blazor客戶端)應用程序。

展示WebAPI如何處理和簽發令牌(JSON web tokens)。以及如何設置各種控制器操作來為客戶端應用程序提供服務。最后,展示如何配置Blazor來使用API和它簽發的令牌來設置應用的身份驗證狀態。

最后也提供我學習本文跟隨作者所寫的源碼(GITHUB)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM