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

ASP.NET Core MVC中员工控制器创建的用户无法登录问题

问题:创建员工同步生成用户后无法登录

在ASP.NET Core MVC项目中,通过EmployeeController的Create方法创建员工时,同步生成ApplicationUser记录到数据库,但使用该用户登录时提示Invalid Login Attempt,而通过注册页面创建的用户可正常登录。

相关代码

EmployeeController的Create方法

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,EmployeeRole,Department,Name,Phone,Address,Email,JoinedDate,Salary")] Employee employee)
{            
    var defaultUser = new ApplicationUser
        {
            UserName = employee.Name,
            Email = employee.Email, 
            PhoneNumber = employee.Phone,
            Address = employee.Address,
            EmployeeRole = employee.EmployeeRole,
            Department = employee.Department,
            EmailConfirmed = true,
            PhoneNumberConfirmed = true
        };

    if (_userManager.Users.All(u =>u.Email != defaultUser.Email))
    {
        var user = await _userManager.FindByEmailAsync(defaultUser.Email);

        if (user == null)
        {
            await _userManager.CreateAsync(defaultUser, "123Pa$$word.");
        }
    }

    if (ModelState.IsValid)
    {
        _context.Add(employee);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }

    return View(employee);
}

Program.cs中的Identity依赖注入

var connectionString = builder.Configuration.GetConnectionString("LocalDatabase");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
           .AddEntityFrameworkStores<ApplicationDbContext>()           
           .AddDefaultUI()
           .AddDefaultTokenProviders();

ApplicationDbContext代码

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
    {
    }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<HRMS.Models.Employee> Employees { get; set; }
    public DbSet<HRMS.Models.Service> Services { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.HasDefaultSchema("Identity");

        builder.Entity<IdentityUser>(entity =>
        {
            entity.ToTable(name: "User");
        });

        builder.Entity<IdentityRole>(entity =>
        {
            entity.ToTable(name: "Role");
        });

        builder.Entity<IdentityUserRole<string>>(entity =>
        {
            entity.ToTable("UserRoles");
        });

        builder.Entity<IdentityUserClaim<string>>(entity =>
        {
            entity.ToTable("UserClaims");
        });

        builder.Entity<IdentityUserLogin<string>>(entity =>
        {
            entity.ToTable("UserLogins");
        });

        builder.Entity<IdentityRoleClaim<string>>(entity =>
        {
            entity.ToTable("RoleClaims");
        });

        builder.Entity<IdentityUserToken<string>>(entity =>
        {
            entity.ToTable("UserTokens");
        });
    }
}

注册页面代码(Registration.cs)

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl ??= Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    if (ModelState.IsValid)
    {
        var user = CreateUser();

        await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
        await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
        var result = await _userManager.CreateAsync(user, Input.Password);

        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }
    // 省略后续代码
}

问题原因

  1. 用户名不匹配:注册页面将UserName设为用户邮箱,但Create方法中UserName设为员工姓名。ASP.NET Core Identity默认用UserName做登录验证,若用户登录时输入邮箱,系统找不到对应UserName,导致登录失败。
  2. 未检查用户创建结果:Create方法调用_userManager.CreateAsync后未验证操作是否成功,若存在密码规则不匹配等隐性错误,会导致用户记录生成但密码未正确哈希存储。
  3. 冗余的重复检查:先通过_userManager.Users.All判断邮箱是否存在,再调用FindByEmailAsync,逻辑冗余且可能因异步操作导致数据不一致。

解决方案

方案1:统一用户名与邮箱(推荐)

修改Create方法中ApplicationUser的UserName赋值,与注册页面保持一致,设为员工邮箱:

var defaultUser = new ApplicationUser
{
    UserName = employee.Email, // 改为邮箱,和注册页逻辑一致
    Email = employee.Email, 
    PhoneNumber = employee.Phone,
    Address = employee.Address,
    EmployeeRole = employee.EmployeeRole,
    Department = employee.Department,
    EmailConfirmed = true,
    PhoneNumberConfirmed = true
};

// 简化重复检查逻辑
var existingUser = await _userManager.FindByEmailAsync(defaultUser.Email);
if (existingUser == null)
{
    var createResult = await _userManager.CreateAsync(defaultUser, "123Pa$$word.");
    // 检查创建结果,处理错误
    if (!createResult.Succeeded)
    {
        foreach (var error in createResult.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
        return View(employee);
    }
}

方案2:配置Identity允许邮箱登录

若需保留UserName为员工姓名,可配置Identity支持通过邮箱登录:
在Program.cs中修改Identity配置:

builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.User.RequireUniqueEmail = true;
    options.SignIn.RequireConfirmedAccount = false; // 根据业务需求调整
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();

// 配置登录验证逻辑,支持邮箱登录
builder.Services.AddScoped<SignInManager<ApplicationUser>>();

同时,修改登录逻辑,优先通过邮箱查找用户:

// 登录方法示例
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
    user = await _userManager.FindByNameAsync(Input.Email);
}
if (user != null && await _userManager.CheckPasswordAsync(user, Input.Password))
{
    await _signInManager.SignInAsync(user, Input.RememberMe);
    return LocalRedirect(returnUrl);
}
ModelState.AddModelError(string.Empty, "Invalid Login Attempt");

额外建议

  • 避免硬编码密码,可通过配置文件存储默认初始密码。
  • 确保密码符合Identity默认规则(至少6位,包含大小写、数字、特殊字符)。
  • 生产环境中开启邮箱验证(RequireConfirmedEmail),提升账号安全性。

内容的提问来源于stack exchange,提问作者soe thein

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:27:33