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

Blazor WebAssembly+ASP.NET Core9 Cookie认证:LoginAsync始终返回False

问题描述

我正在使用Blazor WebAssembly和ASP.NET Core 9 Web API开发全栈应用,已从localStorage令牌管理切换为HttpOnly Cookie认证。调用客户端LoginAsync方法时,API成功返回200 OK,且accessToken Cookie已在浏览器中正确设置(通过DevTools验证),但LoginAsync始终返回False,导致UI无法更新为已认证状态。

登录页面代码

<div class="login-page">
    <div class="login-box">
        <div class="login-header">
            <h1>Hoş Geldiniz</h1>
        </div>

        <EditForm Model="_model" OnSubmit="HandleLogin" class="login-form">
            <DataAnnotationsValidator />
            <ValidationSummary />

            <div class="form-group">
                <label>Kullanıcı Adı</label>
                <InputText @bind-Value="_model.UserName" class="form-control" placeholder="kullanici_adi" />
                <ValidationMessage For="() => _model.UserName" />
            </div>

            <div class="form-group">
                <label>Şifre</label>
                <InputText type="password" @bind-Value="_model.Password" class="form-control" placeholder="••••••••" />
                <ValidationMessage For="() => _model.Password" />
            </div>

            @if (!string.IsNullOrWhiteSpace(_error))
            {
                <div class="alert alert-danger login-error">
                    <i class="bi bi-exclamation-triangle-fill"></i> @_error
                </div>
            }

            <button type="submit" disabled="@_isBusy" class="login-button">
                @if (_isBusy)
                {
                    <span class="spinner"></span>
                }
                Giriş Yap
            </button>

            <div class="register-link">
                Hesabınız yok mu? <a href="/create-user">Kayıt Ol</a>
            </div>
        </EditForm>
    </div>
</div>
@code {
    private LoginDto _model = new();
    private bool _isBusy;
    private string? _error;

    private HttpClient _apiClient;

    protected override void OnInitialized()
    {
        _apiClient = HttpClientFactory.CreateClient("Testify.API");
    }

    private async Task HandleLogin()
    {
        _isBusy = true;
        _error = null;

        try
        {
            var response = await _apiClient.PostAsJsonAsync("api/auth/login", _model);

            if (response.IsSuccessStatusCode)
            {
                NotifyAuthStateChanged();
                NavigationManager.NavigateTo("/", forceLoad: true);
            }
            else
            {
                _error = await response.Content.ReadAsStringAsync();
                if (string.IsNullOrWhiteSpace(_error))
                {
                    _error = "Kullanıcı adı veya şifre hatalı";
                }
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"Login error: {ex}");
            _error = "Giriş sırasında bir hata oluştu";
        }
        finally
        {
            _isBusy = false;
        }
    }

    private void NotifyAuthStateChanged()
    {
        if (QuizAuthStateProvider is QuizAuthStateProvider quizAuth)
        {
            quizAuth.NotifyStateChanged();
        }
    }
}

QuizAuthStateProvider代码

public class QuizAuthStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private ClaimsPrincipal _currentUser = new(new ClaimsIdentity());

    public QuizAuthStateProvider(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
    }

    public async Task<bool> LoginAsync(string username, string password)
    {
        // Cookie otomatik olarak tarayıcıya set edilecek
        var response = await _httpClient.PostAsJsonAsync("/api/auth/login", new { username, password });

        if (response.IsSuccessStatusCode)
        {
            // Sunucudan gelen cookie ile artık kimlik doğrulandı
            _currentUser = new ClaimsPrincipal(new ClaimsIdentity(
                new[] { new Claim(ClaimTypes.Name, username) },
                "Cookies"));

            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
            return true;
        }

        return false;
    }

    public async Task LogoutAsync()
    {
        await _httpClient.PostAsync("/api/auth/logout", null);
        _currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    public void NotifyStateChanged()
    {
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }
}

AuthLoggingHandler代码

public class AuthLoggingHandler : DelegatingHandler
{
    private readonly ILogger<AuthLoggingHandler> _logger;
    private readonly IJSRuntime _jsRuntime;

    // Sadece DI ile alınan parametreler, HttpMessageHandler kaldırıldı
    public AuthLoggingHandler(IJSRuntime jsRuntime, ILogger<AuthLoggingHandler> logger)
    {
        _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        _logger.LogDebug("Initiating request to {Method} {Uri}",
            request.Method, request.RequestUri);

        var response = await base.SendAsync(request, cancellationToken);

        _logger.LogDebug("Received {StatusCode} response from {Uri}",
            response.StatusCode, request.RequestUri);

        return response;
    }
}

Credentials.js代码

// POST isteği, çerezlerle birlikte
window.postWithCredentials = async function (url, body) {
    try {
        const response = await fetch(url, {
            method: "POST",
            credentials: "include",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(body)
        });
        if (!response.ok) throw new Error("HTTP " + response.status);
        return await response.json();
    } catch (err) {
        console.error("postWithCredentials error:", err);
        throw err;
    }
};

// Çerezleri döndürür
window.getCookies = function () {
    return document.cookie;
};

// GET isteği, çerezlerle birlikte
window.getWithCredentials = async function (url) {
    try {
        const response = await fetch(url, {
            method: "GET",
            credentials: "include"
        });
        if (!response.ok) throw new Error("HTTP " + response.status);
        return await response.json();
    } catch (err) {
        console.error("getWithCredentials error:", err);
        throw err;
    }
};

// Sadece giriş yapıldıysa me fonksiyonunu çağır
window.callMeIfLoggedIn = async function (meUrl) {
    if (localStorage.getItem("isLoggedIn") === "true") {
        try {
            const user = await window.getWithCredentials(meUrl);
            // Kullanıcı bilgilerini işle
            console.log("Kullanıcı:", user);
        } catch (err) {
            // Hata yönetimi
            console.warn("Me endpoint hatası:", err);
        }
    } else {
        // Giriş yapılmamış
        console.log("Kullanıcı giriş yapmamış.");
    }
};

CORS配置代码

public static void ConfigureApplicationCookie(this IServiceCollection services)
{
    services.ConfigureApplicationCookie(options =>
    {
        options.Cookie.HttpOnly = true; // HttpOnly cookie olarak ayarla
        options.ExpireTimeSpan = TimeSpan.FromDays(7); // 7 gün geçerli olsun
        options.SlidingExpiration = true; // Kaydırmalı geçerlilik süresi
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS zorunlu
        options.Cookie.SameSite = SameSiteMode.None; // SameSite özelliğini sıkı yap
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:40:53