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

ASP.NET Core 8 Web API并发异常及密码重置无效问题求助

问题描述
  • 环境:VS 2022 + ASP.NET Core 8.0 Web API,集成Identity API,自定义了UserManager包装类
  • DbContext配置:通过依赖注入注册ExpIdentityDbContext(继承自IdentityDbContext),代码如下:
AddScoped<ExpIdentityDbContext>()
.AddDbContext<ExpIdentityDbContext>(options =>
{
  options.UseSqlServer(configuration.GetConnectionString("ExpUsers"));
  options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTrackingWithIdentityResolution);
});
  • 额外配置:同一数据库存在另一个管理非Identity表的DbContext,启用Swagger调试接口
  • 异常情况:
    • 登录、创建/查询用户功能正常,但修改密码时抛出「Optimistic concurrency failure, object has been modified」异常
    • 重置密码时提示「Invalid token」,已通过userManager.GeneratePasswordResetTokenAsync生成令牌,但问题未解决
解决方案

一、解决「Optimistic concurrency failure」异常

该问题核心是EF Core全局无跟踪配置与Identity并发机制冲突,可从以下几点调整:

  1. 移除重复的DbContext注册
    Identity内部会自动处理IdentityDbContext的生命周期,同时用AddScoped和AddDbContext注册会导致同一请求内出现多个DbContext实例,引发跟踪冲突。保留AddDbContext配置,删除单独的AddScoped<ExpIdentityDbContext>()。

  2. 为Identity操作启用实体跟踪
    NoTrackingWithIdentityResolution是全局无跟踪配置,但Identity的用户更新操作需要实体跟踪来处理ConcurrencyStamp并发戳。可通过两种方式调整:

    • 单操作手动启用跟踪:
      // 修改密码时,获取带跟踪的用户实例
      var user = await _userManager.Users.AsTracking().FirstOrDefaultAsync(u => u.Id == userId);
      if (user == null) { /* 处理用户不存在逻辑 */ }
      
      var result = await _userManager.ChangePasswordAsync(user, oldPassword, newPassword);
      
    • DbContext层面单独配置:
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          base.OnModelCreating(modelBuilder);
          // 为Identity用户实体强制启用跟踪
          modelBuilder.Entity<IdentityUser>().HasQueryTrackingBehavior(QueryTrackingBehavior.TrackAll);
      }
      
  3. 手动刷新并发戳
    如果仍出现并发错误,可在修改前从数据库重新获取最新的用户并发戳:

    var user = await _userManager.FindByIdAsync(userId);
    if (user != null)
    {
        await _dbContext.Entry(user).ReloadAsync();
        var result = await _userManager.ChangePasswordAsync(user, oldPassword, newPassword);
    }
    

二、解决「Invalid token」问题

重置密码令牌无效通常与用户状态、加密配置或令牌传递过程有关,排查方向:

  1. 确保令牌生成与验证用同一UserManager实例
    检查UserManager包装类是否注册为Scoped生命周期,避免单例导致的状态不一致。同时确认生成令牌后,用户未修改过密码、邮箱等会触发SecurityStamp变更的信息。

  2. 生成令牌前获取最新用户实例
    必须从数据库获取最新的用户数据生成令牌,不能使用缓存或无跟踪的旧实例:

    var user = await _userManager.FindByEmailAsync(email);
    if (user == null) { /* 处理用户不存在逻辑 */ }
    var token = await _userManager.GeneratePasswordResetTokenAsync(user);
    
  3. 检查DataProtection配置
    Identity令牌依赖DataProtection服务,若应用重启或多实例部署,需配置持久化密钥避免令牌失效:

    // 在Program.cs中添加配置
    builder.Services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"C:\AppKeys")) // 密钥持久化路径
        .SetApplicationName("YourAppUniqueName"); // 确保不同环境应用名称一致
    

    单实例调试时,若生成令牌后重启过应用,旧令牌会因密钥重置失效,需重新生成。

  4. 处理令牌URL编码问题
    通过Swagger传递令牌时,特殊字符(如+、/、=)可能被转义,验证前需解码:

    var decodedToken = HttpUtility.UrlDecode(token);
    var result = await _userManager.ResetPasswordAsync(user, decodedToken, newPassword);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 15:07:03