我通过选择渲染模式“自动(服务器和 WebAssembly)”并使用“个人帐户”作为身份验证创建了 Blazor 8 应用程序。该应用程序在“管理员”和“用户”等各种角色下运行良好。
Blazor 服务器项目中还有一个 API 控制器,它也使用 Authorize 属性进行修饰。 Blazor 客户端应用程序正在使用 HttpClient 调用 API。如果 API 控制器上没有 Authorize 属性,Blazor 客户端应用程序可以获取数据,但当我使用 Authorize 属性时,它无法调用 API。 相同的身份验证方案如何与 API 控制器配合使用?如何使 API 可从 Blazor 客户端应用程序成功调用?
服务器项目中的Program.cs如下:
using BlazorAppAuthenticationDemo.Client.Services;
using BlazorAppAuthenticationDemo.Components;
using BlazorAppAuthenticationDemo.Components.Account;
using BlazorAppAuthenticationDemo.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<TemplateService>();
builder.Services.AddControllers();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
builder.Services.AddScoped(http => new HttpClient
{
BaseAddress = new Uri(builder.Configuration.GetSection("BaseAddress").Value!)
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapControllers();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(BlazorAppAuthenticationDemo.Client._Imports).Assembly);
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.Run();
客户端项目中的Program.cs如下:
using BlazorAppAuthenticationDemo.Client;
using BlazorAppAuthenticationDemo.Client.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddSingleton<AuthenticationStateProvider, PersistentAuthenticationStateProvider>();
builder.Services.AddScoped<TemplateService>();
builder.Services.AddScoped(http => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress),
});
await builder.Build().RunAsync();
TemplateService.cs 来使用 API:
using BlazorAppAuthenticationDemo.Shared.Models;
using System.Net.Http.Json;
namespace BlazorAppAuthenticationDemo.Client.Services;
public class TemplateService(HttpClient httpClient)
{
private readonly HttpClient _httpClient = httpClient;
public async Task<List<TemplateDTO>?> All()
{
var templates = await _httpClient.GetAsync($"api/Templates/all");
List<TemplateDTO>? data = null;
if (templates.IsSuccessStatusCode)
{
data = await templates.Content.ReadFromJsonAsync<List<TemplateDTO>?>();
}
return data;
}
}
TemplatesController.cs 有一个端点:
using BlazorAppAuthenticationDemo.Shared.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace BlazorAppAuthenticationDemo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TemplatesController : ControllerBase
{
private readonly List<TemplateDTO> _templates = [
new TemplateDTO {Id =1, Name = "Test 1", Content = "This is first template." },
new TemplateDTO { Id =2, Name = "Test 2", Content = "This is second template."},
new TemplateDTO { Id =3, Name = "Test 3", Content = "This is third template."}
];
[HttpGet("all")]
[Authorize(Roles = "Administrators")]
public async Task<ActionResult<List<TemplateDTO>>> All()
{
await Task.CompletedTask;
return Ok(_templates);
}
}