我有类似的代码在以前的版本(如 .NET 7)中工作,但当我将
@attribute [Authorize]
添加到页面时,我收到“HTTP ERROR 401”错误。
这是 GIT hub 中的示例项目。
https://github.com/sbakula/BlazorServerCustomAuthTest
我的
Program.cs
代码;
using BlazorServerCustomAuthTest.Auth;
using BlazorServerCustomAuthTest.Components;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "XXXX",
ValidIssuer = "XXXX",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("XXXX"))
};
});
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddScoped<AuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>(
provider => provider.GetRequiredService<AuthStateProvider>()
);
builder.Services.AddScoped<ILoginService, AuthStateProvider>(
provider => provider.GetRequiredService<AuthStateProvider>()
);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Routes.razor
代码:
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<NotAuthorized>
<text>Custom not authorized...</text>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
我的
AuthStateProvider.cs
:
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.Http.Headers;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using BlazorServerCustomAuthTest.Models;
namespace BlazorServerCustomAuthTest.Auth
{
public class AuthStateProvider : AuthenticationStateProvider, ILoginService
{
private ProtectedSessionStorage ProtectedSessionStore;
static AuthenticationState Anonymous =>
new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
public AuthStateProvider(ProtectedSessionStorage ProtectedSessionStore)
{
this.ProtectedSessionStore = ProtectedSessionStore;
}
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
UserProfile? user;
var result = await ProtectedSessionStore.GetAsync<UserProfile>("UserProfile");
if (result.Success)
{
user = result.Value;
return await Task.FromResult(BuildAuthenticationState(user));
}
else
{
return Anonymous;
}
}
public async Task Login(UserProfile user)
{
await ProtectedSessionStore.SetAsync("UserProfile", user);
var authState = BuildAuthenticationState(user);
NotifyAuthenticationStateChanged(Task.FromResult(authState));
}
public async Task Logout()
{
await ProtectedSessionStore.DeleteAsync("UserProfile");
NotifyAuthenticationStateChanged(Task.FromResult(Anonymous));
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
static AuthenticationState BuildAuthenticationState(UserProfile? userProfile)
{
if (userProfile is null)
{
return Anonymous;
}
else
{
var claims = new List<Claim> { };
claims.Add(new Claim(ClaimTypes.Name, userProfile.UserName));
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")));
}
}
}
}
ILoginService.cs
:
using BlazorServerCustomAuthTest.Models;
namespace BlazorServerCustomAuthTest.Auth
{
public interface ILoginService
{
Task Login(UserProfile user);
Task Logout();
}
}
_Imports.razor
:
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorServerCustomAuthTest
@using BlazorServerCustomAuthTest.Components
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
App.razor
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="BlazorServerCustomAuthTest.styles.css" />
<HeadOutlet @rendermode="new InteractiveServerRenderMode( prerender: false )" />
</head>
<body>
<Routes @rendermode="new InteractiveServerRenderMode( prerender: false )" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
我相信您正在尝试集成自定义身份验证机制,以便当没有用户登录 blazor 应用程序时,它将重定向到登录页面。但是您在
Program.cs
中所做的是添加 JWT 身份验证,这将需要请求标头中的令牌,否则返回 401 错误。
我修改了如下代码
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "Test.com",
ValidIssuer = "Test.com",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this is my custom Secret key for authentication"))
};
});
并生成一个jwt令牌来向主页发送Get请求,我可以获得200个代码和html内容响应。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidXNlcm5hbWUxIiwianRpIjoiMTg2MmIyYjMtMDhiYy00ZDExLThkNDctOWRiNTExMjEzMzYzIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzI3ODU4OTQ1LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.7oXqbUCX8iumocwP0dAlIeNruaofaRK_3QqwLL-PHT0
您应该拥有一个自定义的 AuthenticationStateProvider。您可以查看本教程添加登录组件,然后在该组件中调用
NotifyAuthenticationStateChanged
方法来更新身份验证状态。