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

ASP.NET Identity登录成功后User.Identity?.IsAuthenticated仍返回false

ASP.NET Identity登出流程异常:登录后User.Identity?.IsAuthenticated始终返回false

基于ASP.NET Identity搭建的项目中,用户成功登录后,在登出页面(logout.cshtml)内的User.Identity?.IsAuthenticated始终返回false,导致登出按钮无法正常渲染。以下是相关代码及修复方案:


相关代码片段

program.cs

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddClaimsPrincipalFactory<ApplicationClaimsPrincipalFactory>()
    .AddDefaultTokenProviders();

var cert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "mycert.pfx"), "password:!");

builder.Services.AddIdentityServer(options =>
    {
        options.IssuerUri = builder.Configuration.GetValue<string>("IdentityServer:IssuerUri");
    })
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
    .AddSigningCredential(cert);

builder.Services.AddAuthentication()
    .AddIdentityServerJwt();
builder.Services.AddSignalR();

builder.Services.AddResponseCompression(opts =>
{
    opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
          new[] { "application/octet-stream" });
});

builder.Services.AddTransient<IProfileService, ProfileService>();

builder.Services.AddSwaggerGen();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

builder.Services.TryAddEnumerable(
            descriptor: ServiceDescriptor.Singleton<
                IPostConfigureOptions<JwtBearerOptions>,
                ConfigureJwtBearerOptions>());

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
    app.UseWebAssemblyDebugging();
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseResponseCompression();
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseRouting();

app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapControllers();
});

app.MapFallbackToFile("index.html");

app.Run();

ApplicationClaimsPrincipalFactory.cs

public class ApplicationClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public ApplicationClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor)
    : base(userManager, roleManager, optionsAccessor)
    {
    }

    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
    {
        var identity = await base.GenerateClaimsAsync(user);
        identity.AddClaim(new Claim("DisplayName", $"{user.FullName}"));
        identity.AddClaim(new Claim(ClaimTypes.Name, $"{user.UserName}"));
        identity.AddClaim(new Claim("userid", $"{user.Id}"));
        return identity;
    }
}

login.cshtml.cs

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = string.IsNullOrEmpty(returnUrl) ? Url.Content("~/") : HttpUtility.UrlDecode(returnUrl) ;

    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var user = await userManager.FindByEmailAsync(Input.Email);
        var result = await _signInManager.PasswordSignInAsync(user, Input.Password, Input.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return Redirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

logout.cshtml

<header>
    <h1>@ViewData["Title"]</h1>
    @{
        if (User.Identity?.IsAuthenticated ?? false)
        {
            <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/", new { area = "" })" method="post">
                <button type="submit" class="nav-link btn btn-link text-dark">Click here to Logout</button>
            </form>
        }
        else
        {
            <p>You have successfully logged out of the application.</p>
        }
    }
</header>

修复方案

1. 调整中间件顺序

UseIdentityServer已经内置了认证逻辑,无需重复调用UseAuthentication,且中间件顺序必须严格遵循:
修改program.cs中的中间件部分:

app.UseRouting();

// 正确顺序:先IdentityServer,再Authorization
app.UseIdentityServer();
app.UseAuthorization();

// 移除多余的 app.UseAuthentication(); 行

2. 为登出页面添加授权标记

在logout.cshtml.cs的类上添加[Authorize]属性,确保只有已认证用户能访问该页面,从而正确填充User.Identity:

[Authorize]
public class LogoutModel : PageModel
{
    // 原有代码保持不变
}

3. 明确认证默认Scheme

在AddAuthentication中指定默认Scheme,避免IdentityServer与ASP.NET Identity的Scheme冲突:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
    options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
})
.AddIdentityServerJwt();

4. 调试验证登录状态

在登录成功后添加日志输出,确认用户认证状态:
在login.cshtml.cs的result.Succeeded分支中添加:

if (result.Succeeded)
{
    _logger.LogInformation("User logged in.");
    // 输出当前用户认证状态与Claims
    _logger.LogInformation("User Authenticated: {IsAuthenticated}", User.Identity?.IsAuthenticated);
    var claims = User.Claims.Select(c => $"{c.Type}: {c.Value}");
    _logger.LogInformation("User Claims: {Claims}", string.Join(", ", claims));
    return Redirect(returnUrl);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 15:05:08