You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BlazorServer中AuthenticationStateProvider与AuthorizeAttribute异常

Blazor Server 自定义AuthenticationStateProvider认证授权问题

初始问题

在Blazor Server应用(非WebAssembly)中通过AuthenticationStateProvider实现认证授权,完成大部分功能后遇到两个核心问题:

  • 页面刷新后授权完全丢失,必须重新登录;
  • Authorize Attribute机制失效,登录后访问带有@attribute [Authorize(Roles = "SuperAdmin")]的页面仍被重定向至登录页。

应用使用Blazorise组件,部分页面需启用交互模式。

初始版本代码

Program.cs

using Blazored.Toast;
using Blazorise;
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;

// 这是Blazor Server应用(非WebAssembly应用)
var builder = WebApplication.CreateBuilder(args);

// 添加服务默认配置及Aspire组件
builder.AddServiceDefaults();

// 向容器添加服务
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.Name = "auth_cookie";
        options.LoginPath = "/";
        options.LogoutPath = "/logout";
        options.Cookie.MaxAge = TimeSpan.FromMinutes(90);
        options.AccessDeniedPath = "/accessdenied";
    });
builder.Services.ConfigureApplicationCookie(ops =>
{
    ops.ExpireTimeSpan = TimeSpan.FromMinutes(30);
    ops.SlidingExpiration = true;
}); 
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddOutputCache();
builder.Services.AddSingleton<GlobalUserInfo>();
builder.Services.AddBlazoredToast();
builder.Services
    .AddBlazorise(options =>
    {
        options.Immediate = true;
    })
    .AddBootstrap5Providers()
    .AddFontAwesomeIcons();
builder.Services.ConfigureApplicationCookie(ops =>
{
    ops.ExpireTimeSpan = TimeSpan.FromMinutes(30);
    ops.SlidingExpiration = true;
});
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // 默认HSTS值为30天,生产环境可根据需求修改
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseOutputCache();
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.MapDefaultEndpoints();
app.Run();

Login.razor

@code {

    async void Authenticate()
    {
        var result = await LoginApi.Login(login);

        ((CustomAuthStateProvider)AuthenticationStateProvider)
                    .AuthenticateUser(user.UserName, user.Role);
        navigationManager.NavigateTo("/home"); 
    }
}

MyAttribute.razor

@attribute [Authorize(Roles = "SuperAdmin")]

更新后的状态

经过调试后,初始问题已解决:Authorize Attribute生效,页面刷新后授权保留,但出现新问题:同一浏览器仅支持单用户登录(跨浏览器多用户情况未验证)。

核心实现方式为拦截授权请求并与已登录用户对比,但CustomAuthStateProvider注册为单例导致多用户支持失效,改用Scoped模式也未解决该问题。

更新后代码

CustomAuthStateProvider.cs

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;

namespace MyApp.Web.Utilities
{
    public class CustomAuthStateProvider : AuthenticationStateProvider
    {
  
        private ClaimsIdentity _identity = new();
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var user = new ClaimsPrincipal(_identity);
            return Task.FromResult(new AuthenticationState(user));
        }

        public void AuthenticateUser(string userIdentifier, string role)
        {
            _identity = new ClaimsIdentity(
            [new Claim(ClaimTypes.Name, userIdentifier), new Claim(ClaimTypes.Role, role),], "Custom Authentication");
            var user = new ClaimsPrincipal(_identity);

            NotifyAuthenticationStateChanged(
                Task.FromResult(new AuthenticationState(user)));
        }

        public async Task LogUserOut()
        {
            _identity = new ClaimsIdentity();
            var user = new ClaimsPrincipal(_identity);
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
        }
    }
}

CustomAuthorizationHandler.cs

using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;

namespace MyApp.Web.Utilities
{
    public class CustomAuthorizationHandler : IAuthorizationMiddlewareResultHandler
    {
        private readonly AuthenticationStateProvider _authStateProvider;

        public CustomAuthorizationHandler(AuthenticationStateProvider authStateProvider)
        {
            _authStateProvider = authStateProvider;
        }
        public Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
        {
            var result = _authStateProvider.GetAuthenticationStateAsync();
            context.User = result.Result.User;
            return next(context);
        }
    }
}

Program.cs(更新版)

using Blazored.Toast;
using Blazorise;
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
using MyApp.Web.APIClients;
using MyApp.Web.Components;
using MyApp.Web.Utilities;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;

// 这是Blazor Server应用(非WebAssembly应用)
var builder = WebApplication.CreateBuilder(args);

// 添加服务默认配置及Aspire组件
builder.AddServiceDefaults();

// 向容器添加服务
builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddAuthorizationCore();
builder.Services.AddSingleton<AuthenticationStateProvider, CustomAuthStateProvider>(); // 处理认证逻辑
builder.Services.AddScoped<IAuthorizationMiddlewareResultHandler, CustomAuthorizationHandler>(); 
builder.Services.AddOutputCache();
builder.Services.AddBlazoredToast();
builder.Services
    .AddBlazorise(options =>
    {
        options.Immediate = true;
    })
        .AddBootstrap5Providers()
        .AddFontAwesomeIcons();
    builder.Services.ConfigureApplicationCookie(ops =>
    {
        ops.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        ops.SlidingExpiration = true;
    });
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error", createScopeForErrors: true);
        // 默认HSTS值为30天,生产环境可根据需求修改
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseAntiforgery();
    app.UseOutputCache();
    app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
    app.MapDefaultEndpoints();
    app.Run();

内容的提问来源于stack exchange,提问作者Age of Empires

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:32:02