如何将ASP.NET Core 2.0 Identity映射至现有MSSQL数据库架构实现认证授权
嘿,刚好我之前帮朋友处理过一模一样的场景——把ASP.NET Core 2.0 Identity嫁接到已经存在的MSSQL数据库架构上,实现认证授权。给你一步步拆解操作,保证贴合你的现有项目:
首先得把Identity的核心包加到项目里,用NuGet包管理器或者CLI命令都可以:
# 用.NET CLI安装Identity核心包 dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
如果你的项目还没引用EntityFrameworkCore的SQL Server包,也得补上:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Identity默认的实体类是IdentityUser和IdentityRole,但你要映射到自己的数据库表,所以得创建自定义类,用DataAnnotations或者Fluent API指定表名、列名,把现有表的字段和Identity的字段对应起来。
比如你的现有用户表叫Users,主键是UserId,还有FullName、Email这些字段,那自定义User类可以这么写:
using Microsoft.AspNetCore.Identity; using System.ComponentModel.DataAnnotations.Schema; // 指定映射到现有Users表 [Table("Users")] public class AppUser : IdentityUser<int> // 用int作为主键类型,和你现有表的主键类型匹配 { // 把Identity的Id字段映射到现有表的UserId列 [Column("UserId")] public override int Id { get; set; } // 映射现有表的额外字段 [Column("FullName")] public string FullName { get; set; } // 如果你的现有表用Email作为用户名,也可以把UserName映射到Email列 // [Column("Email")] // public override string UserName { get; set; } } // 如果有现有角色表,同样创建自定义Role类 [Table("Roles")] public class AppRole : IdentityRole<int> { [Column("RoleId")] public override int Id { get; set; } // 现有角色表的额外字段,比如RoleDescription [Column("RoleDescription")] public string Description { get; set; } }
你的现有DbContext需要改成继承IdentityDbContext,并传入自定义的User、Role和主键类型。同时在OnModelCreating里配置关联表的映射(比如用户-角色关联表):
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; public class AppDbContext : IdentityDbContext<AppUser, AppRole, int> { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { // 一定要先调用base.OnModelCreating,不然Identity的默认配置会丢失 base.OnModelCreating(builder); // 配置用户-角色关联表映射(如果你的现有表叫UserRoles) builder.Entity<IdentityUserRole<int>>() .ToTable("UserRoles") .HasKey(ur => new { ur.UserId, ur.RoleId }); // 其他Identity相关表(比如UserClaims、UserLogins)如果现有架构里没有,Identity会自动创建;如果有,同样配置映射 // builder.Entity<IdentityUserClaim<int>>().ToTable("UserClaims"); // builder.Entity<IdentityUserLogin<int>>().ToTable("UserLogins"); } // 保留你现有的业务DbSet public DbSet<YourExistingEntity> YourExistingEntities { get; set; } }
打开Startup.cs,在ConfigureServices方法里添加Identity的配置,同时关联你的DbContext:
public void ConfigureServices(IServiceCollection services) { // 配置你的DbContext,用现有连接字符串 services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // 添加Identity服务,指定自定义的User和Role类 services.AddIdentity<AppUser, AppRole>() .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders(); // 用于密码重置、邮箱确认等功能 // 可选:配置Identity的规则(密码复杂度、锁定策略等) services.Configure<IdentityOptions>(options => { options.Password.RequireDigit = true; options.Password.RequireLowercase = true; options.Password.RequiredLength = 6; options.Lockout.MaxFailedAccessAttempts = 5; options.User.RequireUniqueEmail = true; }); // 配置认证Cookie的行为 services.ConfigureApplicationCookie(options => { options.LoginPath = "/Account/Login"; // 未登录时跳转的登录页面 options.AccessDeniedPath = "/Account/AccessDenied"; // 无权限时跳转的页面 options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.SlidingExpiration = true; }); // 保留你已有的MVC和本地化配置 services.AddMvc() .AddLocalization(options => options.ResourcesPath = "Resources"); }
因为要把Identity的表结构(或者映射)同步到现有数据库,得用EF Core的迁移工具。先创建迁移:
dotnet ef migrations add AddIdentityToExistingDatabase
生成迁移文件后,一定要打开文件检查,确保不会删除或修改你现有表的结构(比如有没有意外的DropTable操作)。确认没问题后,执行迁移更新数据库:
dotnet ef database update
注意:执行前最好备份现有数据库,避免迁移出错导致数据丢失!
接下来需要实现登录、注销这些基础功能,比如创建AccountController:
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; public class AccountController : Controller { private readonly UserManager<AppUser> _userManager; private readonly SignInManager<AppUser> _signInManager; // 构造函数注入Identity服务 public AccountController(UserManager<AppUser> userManager, SignInManager<AppUser> signInManager) { _userManager = userManager; _signInManager = signInManager; } // 登录页面GET请求 [HttpGet] public IActionResult Login(string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; return View(); } // 登录POST请求 [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { // 验证用户名密码 var result = await _signInManager.PasswordSignInAsync( model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { // 登录成功,跳转到原请求页面 return RedirectToLocal(returnUrl); } if (result.IsLockedOut) { return View("Lockout"); } else { ModelState.AddModelError(string.Empty, "无效的登录尝试"); return View(model); } } return View(model); } // 注销 [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Logout() { await _signInManager.SignOutAsync(); return RedirectToAction(nameof(HomeController.Index), "Home"); } // 辅助方法:判断是否为本地链接 private IActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction(nameof(HomeController.Index), "Home"); } }
对应的LoginViewModel:
using System.ComponentModel.DataAnnotations; public class LoginViewModel { [Required] [Display(Name = "用户名")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } [Display(Name = "记住我")] public bool RememberMe { get; set; } }
你可以参考ASP.NET Core的默认登录视图,或者自己写一个简单的表单放在Views/Account/Login.cshtml里。
现在可以给需要认证的控制器或Action添加[Authorize]属性,实现权限控制:
// 只有登录用户才能访问这个控制器的所有Action [Authorize] public class AdminController : Controller { public IActionResult Index() { return View(); } // 只有Admin角色的用户才能访问这个Action [Authorize(Roles = "Admin")] public IActionResult ManageUsers() { // 你的用户管理逻辑 return View(); } }
- 密码哈希问题:如果现有用户的密码不是用Identity默认的哈希算法存储的,登录会失败。你可以要么批量把现有密码转换成Identity的哈希格式,要么实现自定义的
IPasswordHasher<AppUser>接口来兼容旧的哈希算法。 - 字段映射检查:一定要确保自定义User/Role类的字段和现有表的列完全对应,比如主键类型、字段名,不然会出现映射错误。
- 迁移检查:生成迁移文件后一定要仔细检查,避免误删现有数据或表结构。
内容的提问来源于stack exchange,提问作者misanthrop

