我有一个 .net 8 Blazor Wasm 应用程序,我正在尝试向其中添加基本的自定义 JWT 授权。 (我们自己数据库中的用户)
但是,当我进入受保护页面时,它只显示带有 401 错误的屏幕。如果我删除授权属性,页面可以正常工作(尽管显然没有受到保护)。
这是页面
@page "/test"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@inject HttpClient Http
@attribute [Authorize]
<h3>Test</h3>
@code {
}
和 Routes.razor
@using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<NotAuthorized>
<h1>Not authorized</h1>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
和自定义身份验证提供商
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
namespace Web.Client.Auth
{
public class CustomAuthStateProvider : AuthenticationStateProvider
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
private readonly NavigationManager _navigationManager;
private readonly AuthenticationState _anonymous;
private readonly JsonSerializerOptions _options;
public CustomAuthStateProvider(HttpClient httpClient, ILocalStorageService localStorage, NavigationManager navigationManager)
{
_httpClient = httpClient;
_localStorage = localStorage;
_navigationManager = navigationManager;
_anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
_options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var token = await _localStorage.GetItemAsync<string>("authToken");
if (string.IsNullOrWhiteSpace(token))
{
return _anonymous;
}
var claims = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>(), "jwtAuthType"));
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
//CheckTimeSinceLogin(claims);
return new AuthenticationState(claims);
}
public void NotifyUserAuthentication(string email)
{
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, email) }, "jwtAuthType"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
}
public void NotifyUserLogout()
{
var authState = Task.FromResult(_anonymous);
NotifyAuthenticationStateChanged(authState);
}
}
}
这是客户端应用程序的 Program.cs
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Components.Authorization;
using RecoWeb.Client.Auth;
using Blazored.LocalStorage;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddHttpClient("VLA.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
await builder.Build().RunAsync();
以及服务器应用程序
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using MyApp.DataService;
using MyApp.DataService.Models;
using MyApp.Interfaces;
using MyApp.Services;
using MyApp.Shared.Dtos;
using MyAppWeb.Client.Auth;
using MyAppWeb.Client.Pages;
using MyAppWeb.Components;
using MyAppWeb.Controllers;
using StockDataImporter;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
builder.Configuration.AddJsonFile($"appsettings.local.json", optional: true);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddControllers(); // Add this line
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
var jwtSettings = config.GetSection("JWTSettings");
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings["validIssuer"],
ValidAudience = jwtSettings["validAudience"],
ClockSkew = new TimeSpan(0, 1, 0),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"]))
};
});
builder.Services.AddDbContext<RecodbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
builder.Services.AddScoped<IDataService, DataService>();
builder.Services.AddScoped<IStockDataImportService, StockDataImportService>();
builder.Services.AddScoped<IPartService, PartService>();
builder.Services.AddScoped<IStockService, StockService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
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.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting(); // Add this line
app.UseAntiforgery();
app.UseAuthorization(); // Add this line
app.MapControllers();
app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(RecoWeb.Client._Imports).Assembly);
app.Run();
我做错了什么?
需要明确的是,我希望页面显示来自routes.razor 的未经授权的内容。
在 .net 7 中从未如此困难...