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

自定义ASP.NET Core Identity注册接口致登录401问题求助

ASP.NET Core Identity自定义注册后登录401问题排查

我在使用Microsoft ASP.NET Core Identity时,为IdentityUser添加了额外属性Group,并实现了自定义注册接口。使用该接口注册用户后,登录会返回401未授权(确认登录参数与注册一致),但用默认的Identity注册接口创建的用户可以正常登录。

自定义注册控制器代码

[AllowAnonymous]
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly UserManager<AppUser> _userManager;
    private readonly SignInManager<AppUser> _signInManager;
    private readonly IUserStore<AppUser> _userStore;

    public UserController(UserManager<AppUser> userManager, SignInManager<AppUser> signInManager, IUserStore<AppUser> userStore)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _userStore = userStore;
    }

    [HttpPost("register")]
    public async Task<ActionResult<UserDto>> Register(RegisterDto registerDto)
    {
        var user = new AppUser()
        {
            UserName = registerDto.Username,
            Email = registerDto.Email,
            Group = registerDto.Group
        };

        await _userStore.SetUserNameAsync(user, registerDto.Username, CancellationToken.None);

        var result = await _userManager.CreateAsync(user,registerDto.Password);
        
        if (result.Succeeded)
        {
            return new UserDto
            {
                Email = registerDto.Email,
                Username = registerDto.Username,
                Group = registerDto.Group
            };
        }
        return BadRequest(result.Errors);
    }
}

相关配置代码

Identity服务扩展类

public static class IdentityServiceExtensions
{
    public static IServiceCollection AddIdentityServices(this IServiceCollection services,IConfiguration config)
    {
        //Add authentication
        services
            .AddAuthentication()
            .AddBearerToken(IdentityConstants.BearerScheme);
        //Add authorization
        services.AddAuthorizationBuilder();
        services
            .AddIdentityCore<AppUser>(opt =>
            {
                opt.User.RequireUniqueEmail = true;
                opt.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";
            })
            .AddEntityFrameworkStores<UserDbContext>()
            .AddApiEndpoints();
        return services;
    }
}

Program.cs

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using User.DataContext;
using UserService.API.Extensions;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration =builder.Configuration;
// Add services to the container.
services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(options =>
{
    options.AddSecurityDefinition("oath2", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    });
    options.OperationFilter<SecurityRequirementsOperationFilter>();
});

//Configure DbContext
services.AddDbContextPool<UserDbContext>(o => o.UseSqlServer(configuration.GetConnectionString("Users")));

//Configure Identity Services
services.AddIdentityServices(configuration);
var app = builder.Build();
app.MapIdentityApi<AppUser>();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

问题原因分析

问题出在自定义注册逻辑中未正确处理Identity的规范化字段:

ASP.NET Core Identity在登录验证时,会将用户输入的用户名/邮箱转换为规范化格式(默认是全大写),然后去数据库匹配NormalizedUserName和NormalizedEmail字段。默认注册接口会自动完成这两个字段的生成与赋值,但自定义注册代码存在两个问题:

  • 手动调用_userStore.SetUserNameAsync跳过了UserManager的规范化逻辑,导致NormalizedUserName字段未被正确设置
  • 未对Email字段执行规范化处理,NormalizedEmail字段为空或不正确

这就导致登录时,Identity用规范化后的用户名/邮箱去数据库查找,找不到对应的用户记录,从而返回401未授权。


修复方案

方案一:移除手动存储用户名的代码,让UserManager自动处理

删除await _userStore.SetUserNameAsync(user, registerDto.Username, CancellationToken.None);这一行代码。因为已经在AppUser实例中设置了UserName,UserManager.CreateAsync内部会自动处理用户名、邮箱的规范化和存储:

修改后的注册方法:

[HttpPost("register")]
public async Task<ActionResult<UserDto>> Register(RegisterDto registerDto)
{
    var user = new AppUser()
    {
        UserName = registerDto.Username,
        Email = registerDto.Email,
        Group = registerDto.Group
    };

    // 移除手动设置UserName的Store调用
    var result = await _userManager.CreateAsync(user, registerDto.Password);
    
    if (result.Succeeded)
    {
        return new UserDto
        {
            Email = registerDto.Email,
            Username = registerDto.Username,
            Group = registerDto.Group
        };
    }
    return BadRequest(result.Errors);
}

方案二:手动设置规范化字段(可选)

如果需要保留手动调用Store的逻辑,可通过UserManager的规范化方法手动设置NormalizedUserName和NormalizedEmail:

var user = new AppUser()
{
    UserName = registerDto.Username,
    Email = registerDto.Email,
    Group = registerDto.Group,
    NormalizedUserName = _userManager.NormalizeName(registerDto.Username),
    NormalizedEmail = _userManager.NormalizeEmail(registerDto.Email)
};

验证修复效果

  1. 重新运行程序,用自定义接口注册新用户
  2. 查看数据库AspNetUsers表,确认该用户的NormalizedUserName和NormalizedEmail字段已被正确填充(默认是全大写格式)
  3. 使用该用户登录,验证是否能正常获取Token

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 15:48:12