ASP.NET 4.5.2 MVC迁移至Core6:Identity转JWT并保留用户密码
迁移至ASP.NET Core 6 + 自定义用户+JWT并保留原密码有效性
1. 复刻原用户表结构到Core项目
先在ASP.NET Core 6项目中创建自定义用户实体,对齐原数据库Identity用户表的核心字段:
public class AppUser { public int Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public string Email { get; set; } // 原表中的其他自定义字段直接复制 }
通过EF Core配置DbContext,映射到原数据库的用户表(默认表名AspNetUsers):
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<AppUser> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<AppUser>().ToTable("AspNetUsers"); modelBuilder.Entity<AppUser>().HasKey(u => u.Id); // 按需配置字段长度、索引等 } }
2. 实现兼容原Identity的密码验证
ASP.NET Core的PasswordHasher<AppUser>原生支持验证ASP.NET Identity 2.x(对应.NET 4.5.2)生成的密码哈希,无需手动实现旧算法。封装用户服务类处理验证逻辑:
public class UserService { private readonly AppDbContext _dbContext; private readonly PasswordHasher<AppUser> _passwordHasher; public UserService(AppDbContext dbContext) { _dbContext = dbContext; _passwordHasher = new PasswordHasher<AppUser>(); } public async Task<AppUser> ValidateUserAsync(string userName, string password) { var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.UserName == userName); if (user == null) return null; // 验证旧哈希,支持自动识别格式 var verifyResult = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password); return verifyResult is PasswordVerificationResult.Success or PasswordVerificationResult.SuccessRehashNeeded ? user : null; } // 可选:用户登录时自动将旧哈希升级为Core的新格式 public async Task UpgradePasswordHashAsync(AppUser user, string password) { var result = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password); if (result == PasswordVerificationResult.SuccessRehashNeeded) { user.PasswordHash = _passwordHasher.HashPassword(user, password); _dbContext.Users.Update(user); await _dbContext.SaveChangesAsync(); } } }
3. 配置JWT认证服务
在Program.cs中注册JWT认证相关服务:
var builder = WebApplication.CreateBuilder(args); // 注册DbContext,使用原数据库连接字符串 builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // 注册用户服务 builder.Services.AddScoped<UserService>(); // 配置JWT认证 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])) }; }); builder.Services.AddControllersWithViews(); var app = builder.Build(); // 中间件顺序:认证在前,授权在后 app.UseAuthentication(); app.UseAuthorization(); // 其他中间件配置(静态文件、路由等) app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
在appsettings.json中添加JWT配置项:
{ "ConnectionStrings": { "DefaultConnection": "原数据库连接字符串" }, "Jwt": { "Issuer": "你的服务标识", "Audience": "你的客户端标识", "SecretKey": "至少16位的安全密钥(如your_secure_jwt_secret_123)", "ExpiresInMinutes": 60 } }
4. 实现登录接口生成JWT令牌
创建认证控制器处理登录请求:
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { private readonly UserService _userService; private readonly IConfiguration _config; public AuthController(UserService userService, IConfiguration config) { _userService = userService; _config = config; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginRequest request) { var user = await _userService.ValidateUserAsync(request.UserName, request.Password); if (user == null) return Unauthorized("用户名或密码错误"); // 可选:升级密码哈希格式 await _userService.UpgradePasswordHashAsync(user, request.Password); // 生成JWT令牌 var token = GenerateJwtToken(user); return Ok(new { Token = token }); } private string GenerateJwtToken(AppUser user) { var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Email, user.Email) // 添加自定义Claim(如角色、权限) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:SecretKey"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"], audience: _config["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddMinutes(Convert.ToDouble(_config["Jwt:ExpiresInMinutes"])), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } } public class LoginRequest { public string UserName { get; set; } public string Password { get; set; } }
5. 替换原有身份验证逻辑
将原项目中依赖Identity的用户信息获取逻辑,改为从JWT Claims中提取:
public class HomeController : Controller { [Authorize] // 需要授权的Action添加该特性 public IActionResult SecurePage() { // 从Claims获取当前用户信息 var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var userName = User.FindFirstValue(ClaimTypes.Name); // 业务逻辑处理 return View(); } }
关键注意事项
- 确保原数据库
PasswordHash字段完整迁移,无截断或修改 - 若原项目使用了自定义密码哈希器,需在Core项目中复刻相同的验证逻辑(默认PBKDF2格式无需额外处理)
- 测试阶段用原数据库的真实账号验证密码有效性
- 自动升级哈希格式可提升后续密码存储的安全性
内容的提问来源于stack exchange,提问作者Elchin
相关产品推荐
相关产品推荐

