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

Blazor自定义AuthenticationStateProvider未设置Cookie及页面授权失败问题

Blazor Cookie认证失败(无Cookie创建)的修复方案

核心问题梳理

你的配置存在几个关键问题:Cookie策略冲突、认证状态未通知更新、HttpClient未配置Cookie传递、登录接口冗余特性、Cookie验证逻辑错误,这些共同导致浏览器无法创建Cookie,受保护页面无法识别已认证用户。


具体修复步骤

1. 统一Cookie SameSite策略

Server项目Program.cs中,AddCookie配置的SameSite是Lax,但后续UseCookiePolicy设置的是Strict,两者冲突会导致Cookie无法正常写入。移除单独的CookiePolicy配置,或者将其SameSite改为Lax:

// 删掉这段单独的CookiePolicy配置
// var cookiePolicyOptions = new CookiePolicyOptions
// {
//     MinimumSameSitePolicy = SameSiteMode.Strict,
// };
// app.UseCookiePolicy(cookiePolicyOptions);

// 或者修改为和AddCookie一致的Lax
var cookiePolicyOptions = new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Lax,
};
app.UseCookiePolicy(cookiePolicyOptions);

2. 配置WASM端HttpClient传递Cookie

Client项目Program.cs中,默认HttpClient不会自动携带Cookie,需要添加配置确保认证Cookie能随请求发送:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Logging.SetMinimumLevel(LogLevel.Debug);

// 添加AuthorizationMessageHandler,确保HttpClient携带认证Cookie
builder.Services.AddScoped<AuthorizationMessageHandler>();
builder.Services.AddHttpClient("ServerAPI", client =>
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<AuthorizationMessageHandler>();

// 注册其他服务
builder.Services.AddSingleton<IUserSettingsService, UserSettingsService>();
builder.Services.AddScoped<ToastService>();
builder.Services.AddScoped<ModalService>();

await builder.Build().RunAsync();

同时在Login.razor中注入命名HttpClient:

@inject IHttpClientFactory HttpClientFactory
// ...
private async Task HandleLogin()
{
    var httpClient = HttpClientFactory.CreateClient("ServerAPI");
    var response = await httpClient.PostAsJsonAsync("/api/auth/login", loginModel);
    // ...
}

3. 让AuthenticationStateProvider主动通知状态变化

当前的CustomAuthenticationStateProvider在登录成功后不会通知Blazor更新认证状态,导致UI无法感知用户已登录。修改CustomAuthenticationStateProvider:

namespace Dragon.Authentications
{
    public class CustomAuthenticationStateProvider : AuthenticationStateProvider
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var httpContext = _httpContextAccessor.HttpContext;
            var user = httpContext?.User;
            
            if (user?.Identity != null && user.Identity.IsAuthenticated)
            {
                return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(user)));
            }

            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
        }

        // 添加状态通知方法
        public void NotifyAuthenticationStateChanged()
        {
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }
    }
}

然后在Login.razor的登录成功逻辑中调用这个方法:

if (response.IsSuccessStatusCode)
{
    ((CustomAuthenticationStateProvider)AuthenticationStateProvider).NotifyAuthenticationStateChanged();
    // 强制刷新页面,确保Cookie生效
    Navigation.NavigateTo("/", forceLoad: true);
}

4. 移除登录接口的冗余特性

AuthController的Login方法同时标记了[Authorize]和[AllowAnonymous],虽然[AllowAnonymous]会覆盖,但冗余配置可能导致混淆,直接删掉[Authorize]:

[HttpPost("login")]
[AllowAnonymous] // 只保留这个
public async Task<IActionResult> Login([FromBody] LoginModel request)

5. 修复Cookie验证逻辑的错误

OnValidatePrincipal中,若用户已认证但没有过期Claim,当前逻辑会直接拒绝用户,这是错误的。修改验证逻辑:

o.Events = new CookieAuthenticationEvents
{
    OnValidatePrincipal = ctx =>
    {
        if (ctx.Principal?.Identity?.IsAuthenticated ?? false)
        {
            var expirationClaim = ctx.Principal.FindFirst(ClaimTypes.Expiration)?.Value;
            // 只有当存在过期Claim且已过期时,才拒绝用户
            if (!string.IsNullOrEmpty(expirationClaim) 
                && DateTimeOffset.TryParse(expirationClaim, out var expiration) 
                && expiration < DateTimeOffset.UtcNow)
            {
                ctx.RejectPrincipal();
                return ctx.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            }
        }
        // 未认证的情况交给默认处理,不要主动Reject
        return Task.CompletedTask;
    }
};

6. 调整中间件顺序

Server项目Program.cs中,中间件顺序必须严格遵循:静态文件 → 路由 → Cookie策略 → 认证 → 授权。当前UseAntiforgery放在了UseCookiePolicy之前,调整顺序:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy(); // 移到这里,在认证之前
app.UseAntiforgery();
app.UseAuthentication();
app.UseAuthorization();

7. 确保Login页面的RenderMode正确

Login.razor使用的InteractiveAutoRenderMode(false)可能导致WASM端处理请求,无法正确设置Cookie。改为Server渲染模式:

@rendermode InteractiveServerRenderMode

内容的提问来源于stack exchange,提问作者Prefetcher Shatnawi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:50:56