使用 JWT 的 Blazor 服务器自定义身份验证在 .NET 8 中不起作用

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

我有类似的代码在以前的版本(如 .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 blazor-server-side .net-8.0
1个回答
0
投票

我相信您正在尝试集成自定义身份验证机制,以便当没有用户登录 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

enter image description here

您应该拥有一个自定义的 AuthenticationStateProvider。您可以查看本教程添加登录组件,然后在该组件中调用

NotifyAuthenticationStateChanged
方法来更新身份验证状态。

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