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

ASP.NET Core Identity登录失败后持续返回401未授权问题排查

问题分析与解决方案

问题成因

你的核心问题是错误密码登录一次后,后续正确密码登录仍返回401,主要由以下三点导致:

  1. 中间件顺序错误
    MapIdentityApi 被放置在 UseAuthentication 和 UseAuthorization 之前,导致Identity内置端点无法正确依赖认证授权中间件,引发认证上下文混乱,后续请求被错误拦截。

  2. 默认账户锁定未禁用
    尽管你提到未启用锁定机制,但AddIdentityApiEndpoints默认开启了账户锁定(Lockout.AllowedForNewUsers = true),默认失败次数阈值为5次。即使单次错误不会触发锁定,若测试中多次错误或存在隐性计数逻辑,会导致账户被锁定,返回401。

  3. 自定义Claims工厂逻辑不完整
    你的ZlecajGoUserClaimsPrincipalFactory未调用基类的CreateAsync方法,而是直接调用GenerateClaimsAsync,可能遗漏基础身份Claims(如ClaimTypes.NameIdentifier),导致认证时身份识别失败。


解决步骤

1. 修正中间件顺序

将认证授权中间件移至所有端点映射之前,确保所有接口(包括Identity内置端点)能正确使用认证逻辑:

// Program.cs 调整后
app.UseHttpsRedirection();

// 先启用认证授权中间件
app.UseAuthentication();
app.UseAuthorization();

// 再映射Identity和业务接口
app.MapGroup("/api/identity")
    .WithTags("Identity")
    .MapIdentityApi<User>();

app.MapControllers();

2. 显式配置Identity锁定选项

在AddIdentityApiEndpoints中明确配置锁定规则,彻底禁用或调整阈值:

// AddInfrastructure方法中修改
services.AddIdentityApiEndpoints<User>(options =>
{
    // 完全禁用账户锁定
    options.Lockout.AllowedForNewUsers = false;
    // 若需保留锁定,可调整参数
    // options.Lockout.MaxFailedAccessAttempts = 10;
    // options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
})
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<ZlecajGoUserClaimsPrincipalFactory>()
.AddEntityFrameworkStores<ZlecajGoContext>();

3. 完善自定义Claims工厂逻辑

调用基类CreateAsync方法生成基础Claims,避免遗漏核心身份信息:

// ZlecajGoUserClaimsPrincipalFactory.cs 修改后
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
    // 调用基类方法生成基础身份Claims
    var principal = await base.CreateAsync(user);
    var id = principal.Identity as ClaimsIdentity;
    
    if (id == null) return principal;

    if (user.UserName != null)
        id.AddClaim(new Claim(AppClaimTypes.UserName, user.UserName));
    
    if (user.FullName != null)
        id.AddClaim(new Claim(AppClaimTypes.FullName, user.FullName));
    
    if (user.PhoneNumber != null)
        id.AddClaim(new Claim(AppClaimTypes.PhoneNumber, user.PhoneNumber));
    
    if (user.BirthDate != null)
        id.AddClaim(new Claim(AppClaimTypes.BirthDate, user.BirthDate.Value.ToString("dd-MM-yyyy")));
    
    id.AddClaim(new Claim(AppClaimTypes.IsProfileCompleted, user.IsProfileCompleted.ToString()));

    return principal;
}

4. 强制刷新用户Claims(可选)

用户完善资料后,更新安全戳强制Claims刷新,确保新的IsProfileCompleted Claim能在后续令牌中生效:

// UpdateUserCommandHandler.cs 末尾添加
if (dbUser.IsProfileCompleted)
{
    var userManager = userStore as UserManager<User>;
    if (userManager != null)
    {
        await userManager.UpdateSecurityStampAsync(dbUser);
    }
}

额外排查点

  • 检查数据库AspNetUsers表的LockoutEnd字段,若存在锁定记录,手动设为NULL解锁测试账户;
  • 测试时清除客户端缓存的旧令牌,确保每次登录请求都是全新的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:20:54