ASP.NET Core MVC Cookie认证实现引发无限重定向循环问题
问题描述
我参考微软官方文档及视频教程,在ASP.NET Core MVC项目中实现基础Cookie认证授权系统,遇到以下异常:
- AccessController的Login(GET)方法中,
claimUser.Identity.IsAuthenticated返回true,说明用户已处于认证状态 - 尝试跳转到
Projects/Index时,被[Authorize]属性拦截,触发跳转到登录页的逻辑,进而导致无限重定向循环 - 移除
[Authorize]属性后,可正常跳转到Projects页面
相关代码
Program.cs 中间件配置
using Translator_Project_Management.Database; using Translator_Project_Management.Importers; using Translator_Project_Management.Importers.XML; using Translator_Project_Management.Repositories; using Translator_Project_Management.Repositories.Interfaces; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.DependencyInjection; var builder = WebApplication.CreateBuilder(args); var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); var connectionString = configuration.GetConnectionString("MySqlDatabase"); //Adding authentication services builder.Services.AddAuthentication( CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(option => { option.LoginPath = "/Access/Login"; option.ExpireTimeSpan = TimeSpan.FromMinutes(30); }); // Add services to the container. builder.Services.AddControllersWithViews(); //MySQL builder.Services.AddScoped<MySqlDatabase>(_ => new MySqlDatabase(connectionString)); //Repository services builder.Services.AddTransient<IProjectRepository, ProjectRepository>(); builder.Services.AddTransient<IUserRepository, UserRepository>(); builder.Services.AddTransient<IClientRepository, ClientRepository>(); builder.Services.AddTransient<IFileRepository, FileRepository>(); builder.Services.AddTransient<ILanguageRepository, LanguageRepository>(); builder.Services.AddTransient<ILineRepository, LineRepository>(); // Importer services builder.Services.AddTransient<IImporter, XLIFFImporter>(); builder.Services.AddTransient<IImporter, JSONImporter>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseAuthentication(); app.MapControllerRoute( name: "default", pattern: "{controller=Access}/{action=Login}/{id?}"); app.Run();
AccessController 登录及Cookie设置代码
using Microsoft.AspNetCore.Mvc; using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Translator_Project_Management.Models.Authentication; namespace Translator_Project_Management.Controllers { public class AccessController : Controller { public IActionResult Login() { // Check if user is already logged in ClaimsPrincipal claimUser = HttpContext.User; if (claimUser.Identity.IsAuthenticated) { return RedirectToAction("Index", "Projects"); } return View(); } [HttpPost] public async Task<IActionResult> Login(Login loginDetails) { if(loginDetails.Email == "user@example.com" && loginDetails.Password == "123") { List<Claim> claims = new List<Claim>() { new Claim(ClaimTypes.NameIdentifier, loginDetails.Email), new Claim("OtherProperties", "Example Role") }; ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); AuthenticationProperties properties = new AuthenticationProperties() { AllowRefresh = true, IsPersistent = loginDetails.StayLoggedIn }; await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), properties); return RedirectToAction("Index", "Projects"); } ViewData["ValidateMessage"] = "User not found."; return View(); } } }
ProjectsController 授权配置
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; namespace Translator_Project_Management.Controllers { [Authorize] //Projects is only accessible on user log-in public class ProjectsController : Controller { public IEnumerable<IImporter> _importers; private readonly IProjectRepository _projectRepository; private readonly IClientRepository _clientRepository; private readonly IUserRepository _userRepository; private readonly IFileRepository _fileRepository; private readonly ILineRepository _lineRepository; private readonly ILanguageRepository _languageRepository; private readonly MySqlDatabase _db; // 剩余代码省略 } }
问题原因及解决方案
核心原因
Program.cs中中间件顺序错误:先调用了UseAuthorization,再调用UseAuthentication。
ASP.NET Core的中间件执行顺序严格遵循注册顺序:
UseAuthentication负责解析认证Cookie,填充HttpContext.User的认证信息UseAuthorization则基于已填充的认证信息判断用户是否有权限访问资源
如果先执行授权中间件,此时认证逻辑还未执行,HttpContext.User的认证状态并未被正确识别,[Authorize]会判定用户未认证,触发跳转到登录页;而登录页的GET方法又检测到用户已认证(后续认证中间件执行后),再次跳转回Projects页面,形成无限循环。
解决方案
调整中间件顺序,将UseAuthentication放在UseAuthorization之前:
app.UseRouting(); // 先执行认证,再执行授权 app.UseAuthentication(); app.UseAuthorization();
修改后,授权中间件执行时,用户的认证信息已经被正确解析,[Authorize]会认可已认证状态,允许访问Projects页面。
内容的提问来源于stack exchange,提问作者Tarek ElShennawy
相关产品推荐
相关产品推荐

