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
相关产品推荐
相关产品推荐

