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

ASP.NET Core MVC Cookie认证实现引发无限重定向循环问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:05:12