自定义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) };
验证修复效果
- 重新运行程序,用自定义接口注册新用户
- 查看数据库
AspNetUsers表,确认该用户的NormalizedUserName和NormalizedEmail字段已被正确填充(默认是全大写格式) - 使用该用户登录,验证是否能正常获取Token
内容的提问来源于stack exchange,提问作者IOEnthusiast
相关产品推荐
相关产品推荐

