Blazor Wasm JWT 身份验证问题 .net 8

问题描述 投票:0回答:1

我有一个 .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 中从未如此困难...

blazor-webassembly .net-8.0
1个回答
0
投票

这是一个类似的问题

但是,当我进入受保护页面时,它只显示带有 401错误

检查您的浏览器是否发送了被服务器端的身份验证中间件阻止的 http 请求

您可以尝试为整个应用程序设置 InteractiveWebAssembly 渲染模式,并使用导航管理器导航到目标页面

最后但并非最不重要的一点是,Blazor WebAssembly 的工程设计使用 OAuth 和 OIDC 作为 Blazor WebAssembly 应用程序中身份验证的最佳选择,不推荐基于令牌的身份验证,您可以阅读文档了解更多详细信息

© www.soinside.com 2019 - 2024. All rights reserved.