如何让ASP.NET Identity识别自定义MVC结构替代默认Razor页面?
解决Identity全局使用自定义MVC控制器的问题
问题描述
项目中使用Identity包并引入自定义MVC结构的Identity代码,但Identity仍调用默认Razor页面逻辑。例如角色验证未通过时,跳转至默认登录页而非自定义的Account控制器Login方法,仅部分场景能跳转至自定义MVC代码,无法全局生效。
项目现有Program.cs代码
using App; using App.Services; using m01_Start; using m01_Start.Models; using m01_Start.Services; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); // add services builder.Services.AddSingleton<ProductService>(); builder.Services.AddSingleton<PlanetService>(); builder.Services.AddSingleton<IdentityErrorDescriber, AppIdentityErrorDescriber>(); // Setup dbcontext builder.Services.AddDbContext<AppDbContext>(options => { string conntectString = builder.Configuration.GetConnectionString("AppMvcConnectString"); // System.Console.WriteLine(conntectString); options.UseSqlServer(conntectString); }); // add identity builder.Services.AddDefaultIdentity<AppUser>() .AddRoles<IdentityRole>() // add fix add role error .AddEntityFrameworkStores<AppDbContext>() // .AddDefaultTokenProviders() ; // Config Identity builder.Services.Configure<IdentityOptions>(options => { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequiredLength = 3; options.Password.RequiredUniqueChars = 1; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1); options.Lockout.MaxFailedAccessAttempts = 3; options.Lockout.AllowedForNewUsers = true; // Cấu hình về User. options.User.AllowedUserNameCharacters = // các ký tự đặt tên user "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; options.User.RequireUniqueEmail = true; options.SignIn.RequireConfirmedEmail = true; options.SignIn.RequireConfirmedPhoneNumber = false; options.SignIn.RequireConfirmedAccount = true; }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.AddStatusCodePage(); app.MapAreaControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}", areaName: "ProductManage" ); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.MapRazorPages(); app.Run();
解决方案
1. 替换Identity服务注册方式
AddDefaultIdentity会自动引入默认Razor Pages版Identity UI,改用AddIdentity手动注册服务,避免依赖默认页面:
修改Identity注册代码:
// 替换原AddDefaultIdentity代码 builder.Services.AddIdentity<AppUser, IdentityRole>() .AddRoles<IdentityRole>() .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders();
2. 配置Identity跳转路径
通过ConfigureApplicationCookie指定登录、权限拒绝等场景的MVC路由:
在Program.cs中添加以下配置:
builder.Services.ConfigureApplicationCookie(options => { // 登录跳转路径,指向自定义Account控制器的Login方法 options.LoginPath = "/Account/Login"; // 权限不足时跳转路径 options.AccessDeniedPath = "/Account/AccessDenied"; // 登出跳转路径 options.LogoutPath = "/Account/Logout"; // Cookie基础配置(可选) options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.SlidingExpiration = true; });
3. 清理不必要的Razor Pages依赖(可选)
若项目无需Razor Pages,移除以下代码:
// 移除Razor Pages服务注册 // builder.Services.AddRazorPages(); // 移除Razor Pages路由映射 // app.MapRazorPages();
4. 验证自定义Account控制器路由
确保自定义Account控制器的Action路由正确,示例:
public class AccountController : Controller { private readonly SignInManager<AppUser> _signInManager; public AccountController(SignInManager<AppUser> signInManager) { _signInManager = signInManager; } [HttpGet] public IActionResult Login(string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; return View(); } [HttpPost] public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) { returnUrl ??= Url.Content("~/"); if (ModelState.IsValid) { var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { return LocalRedirect(returnUrl); } ModelState.AddModelError(string.Empty, "无效的登录尝试。"); } return View(model); } // 其他如AccessDenied、Logout等方法... }
同时确认Views/Account目录下存在对应的视图文件(如Login.cshtml)。
5. 保持路由优先级
确保MVC控制器路由在Razor Pages路由之前注册,现有代码中已先执行MapAreaControllerRoute和MapControllerRoute,再执行MapRazorPages,此顺序正确,无需修改。
修改后的完整Program.cs示例
using App; using App.Services; using m01_Start; using m01_Start.Models; using m01_Start.Services; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); // 若无需Razor Pages,可移除该行 // builder.Services.AddRazorPages(); // add services builder.Services.AddSingleton<ProductService>(); builder.Services.AddSingleton<PlanetService>(); builder.Services.AddSingleton<IdentityErrorDescriber, AppIdentityErrorDescriber>(); // Setup dbcontext builder.Services.AddDbContext<AppDbContext>(options => { string conntectString = builder.Configuration.GetConnectionString("AppMvcConnectString"); options.UseSqlServer(conntectString); }); // 替换为AddIdentity注册Identity服务 builder.Services.AddIdentity<AppUser, IdentityRole>() .AddRoles<IdentityRole>() .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders(); // Config Identity builder.Services.Configure<IdentityOptions>(options => { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequiredLength = 3; options.Password.RequiredUniqueChars = 1; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1); options.Lockout.MaxFailedAccessAttempts = 3; options.Lockout.AllowedForNewUsers = true; options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; options.User.RequireUniqueEmail = true; options.SignIn.RequireConfirmedEmail = true; options.SignIn.RequireConfirmedPhoneNumber = false; options.SignIn.RequireConfirmedAccount = true; }); // 配置Identity跳转路径 builder.Services.ConfigureApplicationCookie(options => { options.LoginPath = "/Account/Login"; options.AccessDeniedPath = "/Account/AccessDenied"; options.LogoutPath = "/Account/Logout"; options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.SlidingExpiration = true; }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.AddStatusCodePage(); app.MapAreaControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}", areaName: "ProductManage" ); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // 若无需Razor Pages,可移除该行 // app.MapRazorPages(); app.Run();
内容的提问来源于stack exchange,提问作者Kuj9xst3
相关产品推荐
相关产品推荐

