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

Web Api与Identity Server 4并发请求出现401授权错误求助

并发请求下IdentityServer4授权仅单个请求成功,其余返回401

我使用Web Api结合IdentityServer4做授权验证,单个请求能正常返回200响应;但发送2个及以上并发请求时,仅一个请求返回200,其余均返回401。反之,如果移除授权验证且不携带Bearer token,可以正常发起多个并发请求。

调试代码

async void Main()
{
    List<string> tokens = new List<string> {
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // 已隐藏真实token,实际使用的是带正确scope的新鲜生成token
            };

    Console.WriteLine("=========TaskList==========");

    List<Task> tasks = new List<Task>();

    foreach (var t in tokens)
        tasks.Add(call(t));
    Task.WaitAll(tasks.ToArray()); // 结果:一个200,多个401

    Console.WriteLine("=========Await==========");
    
    foreach (var t in tokens)
        await call(t);      // 结果:全部200

    async Task call(string tok)
    {
        var handler = new HttpClientHandler
        {
            UseDefaultCredentials = true,
            PreAuthenticate = true
        };

        var httpClient = new HttpClient(handler);
    
        httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + tok); // 移除这行后多并发请求正常;即使服务端去掉授权,加这行也会导致并发请求失败
        var url = "https://localhost:44315/api/admin/user/GetUser"; 
        
        var httpResponseMessage = await httpClient.PostAsJsonAsync(url, "myUser");
        Console.WriteLine(httpResponseMessage.StatusCode);
    }
}

控制器代码

[ApiController]
[Authorize(Policy = "AdminApi")]
[Route("api/admin/user")]
public class AdminUserController : ControllerBase
{
    [HttpPost("GetUser")]
    [ProducesResponseType(typeof(UserCreateResponseView), 200)]
    public async Task<IActionResult> GetUser([FromBody] string username)
    {
        var user = new MockUser(username);
        return Ok(user);
    }
}

补充错误堆栈跟踪

[16:34:33 ERR] JWT token验证错误:尝试在上下文配置期间使用该上下文。DbContext实例不能在OnConfiguring内部使用,因为此时它仍在配置中。如果在之前的操作完成前对该上下文启动第二个操作,就会发生这种情况。任何实例成员都不保证是线程安全的。
System.InvalidOperationException: 尝试在上下文配置期间使用该上下文。DbContext实例不能在OnConfiguring内部使用,因为此时它仍在配置中。如果在之前的操作完成前对该上下文启动第二个操作,就会发生这种情况。任何实例成员都不保证是线程安全的。
   在 Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   在 Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   在 Microsoft.EntityFrameworkCore.DbContext.get_Model()
   在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   在 Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include[TEntity,TProperty](IQueryable`1 source, Expression`1 navigationPropertyPath)
   在 IdentityServer4.EntityFramework.Stores.ClientStore.FindClientByIdAsync(String clientId) 位置 C:\local\identity\server4\EntityFramework\src\IdentityServer4.EntityFramework\Stores\ClientStore.cs:第47行
   在 IdentityServer4.Stores.IClientStoreExtensions.FindEnabledClientByIdAsync(IClientStore store, String clientId) 位置 C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Extensions\IClientStoreExtensions.cs:第23行
   在 IdentityServer4.Validation.TokenValidator.ValidateJwtAsync(String jwt, String audience, IEnumerable`1 validationKeys, Boolean validateLifetime) 位置 C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Validation\TokenValidator.cs:第301行

解决方案

从堆栈跟踪可以看出,问题核心是EntityFramework DbContext的线程安全问题:DbContext实例本身不是线程安全的,当多个并发请求共享同一个DbContext实例时,会触发上下文配置冲突,导致JWT验证失败并返回401。

解决步骤:

  1. 检查DbContext生命周期注册:确保IdentityServer4使用的配置库、操作库DbContext是Scoped生命周期(每个请求对应一个实例),而非Singleton。注册时显式指定Scoped更保险:
// 正确示例:注册为Scoped(默认也是Scoped,显式指定避免误配置)
services.AddDbContext<YourIdentityDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("IdentityDb")),
    ServiceLifetime.Scoped);
  1. 规范IdentityServer的EF存储配置:确保配置Store和操作Store时,使用依赖注入的DbContext,不要手动创建实例:
services.AddIdentityServer()
    .AddConfigurationStore(options =>
    {
        options.ConfigureDbContext = b =>
            b.UseSqlServer(Configuration.GetConnectionString("IdentityDb"),
                sql => sql.MigrationsAssembly(typeof(YourIdentityDbContext).Assembly.GetName().Name));
    })
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = b =>
            b.UseSqlServer(Configuration.GetConnectionString("IdentityDb"),
                sql => sql.MigrationsAssembly(typeof(YourIdentityDbContext).Assembly.GetName().Name));
    })
    // 其他IdentityServer配置...
  1. 自定义Store避免手动实例化DbContext:如果有自定义的ClientStore、ResourceStore等实现,必须通过构造函数注入获取Scoped的DbContext实例,禁止自己new DbContext。

额外提示:调试代码中的async void Main虽不是直接诱因,但建议改为async Task Main,避免潜在的线程调度问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:21:14