【问题标题】:How use JWT Bearer Scheme when working with WebApplicationFactory使用 WebApplicationFactory 时如何使用 JWT Bearer Scheme
【发布时间】:2021-05-11 00:11:04
【问题描述】:

我最近更新了一个可以使用的 Web API,以使用 JWT 身份验证,虽然它在我正常运行时可以正常工作,但我似乎无法让我的集成测试正常工作。

我想开始为我的集成测试集成令牌生成选项,但我什至无法让他们抛出 401。

当我在项目中运行任何没有 JWT 的现有集成测试时,我预计会收到 401,因为我没有任何身份验证信息,但实际上我收到了 System.InvalidOperationException : Scheme already exists: Bearer 错误。

我认为这是因为 WebApplicationFactory 的工作方式是在 Startup 类的 ConfigureServices 方法之后运行其 ConfigureWebHost 方法,并且当我在我的 jwt 服务上设置断点时,确实如此被击中两次,但鉴于这是WebApplicationFactory 的构建方式,我不确定这里推荐的选项是什么。值得注意的是,即使我删除了其中一项服务,我仍然会收到错误消息:

var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(JwtBearerHandler));
services.Remove(serviceDescriptor);

我的WebApplicationFactory是基于eshopwebapi工厂的:


    public class CustomWebApplicationFactory : WebApplicationFactory<StartupTesting>
    {
        // checkpoint for respawning to clear the database when spinning up each time
        private static Checkpoint checkpoint = new Checkpoint
        {
            
        };

        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.UseEnvironment("Testing");

            builder.ConfigureServices(async services =>
            {
                // Create a new service provider.
                var provider = services
                    .AddEntityFrameworkInMemoryDatabase()
                    .BuildServiceProvider();

                // Add a database context (LabDbContext) using an in-memory 
                // database for testing.
                services.AddDbContext<LabDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                    options.UseInternalServiceProvider(provider);
                });

                // Build the service provider.
                var sp = services.BuildServiceProvider();

                // Create a scope to obtain a reference to the database
                // context (ApplicationDbContext).
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<LabDbContext>();

                    // Ensure the database is created.
                    db.Database.EnsureCreated();

                    try
                    {
                        await checkpoint.Reset(db.Database.GetDbConnection());
                    }
                    catch
                    {
                    }
                }
            }).UseStartup<StartupTesting>();
        }

        public HttpClient GetAnonymousClient()
        {
            return CreateClient();
        }
    }

这是我的服务注册:

    public static class ServiceRegistration
    {
        public static void AddIdentityInfrastructure(this IServiceCollection services, IConfiguration configuration)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = configuration["JwtSettings:Authority"];
                    options.Audience = configuration["JwtSettings:Audience"];
                });
            
            services.AddAuthorization(options =>
            {
                options.AddPolicy("CanReadPatients", 
                    policy => policy.RequireClaim("scope", "patients.read"));
                options.AddPolicy("CanAddPatients", 
                    policy => policy.RequireClaim("scope", "patients.add"));
                options.AddPolicy("CanDeletePatients", 
                    policy => policy.RequireClaim("scope", "patients.delete"));
                options.AddPolicy("CanUpdatePatients", 
                    policy => policy.RequireClaim("scope", "patients.update"));
            });
        }
    }

这是我的集成测试(我希望目前会抛出 401):

public class GetPatientIntegrationTests : IClassFixture<CustomWebApplicationFactory>
    { 
        private readonly CustomWebApplicationFactory _factory;

        public GetPatientIntegrationTests(CustomWebApplicationFactory factory)
        {
            _factory = factory;
        }

        
        [Fact]
        public async Task GetPatients_ReturnsSuccessCodeAndResourceWithAccurateFields()
        {
            var fakePatientOne = new FakePatient { }.Generate();
            var fakePatientTwo = new FakePatient { }.Generate();

            var appFactory = _factory;
            using (var scope = appFactory.Services.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<LabDbContext>();
                context.Database.EnsureCreated();

                context.Patients.AddRange(fakePatientOne, fakePatientTwo);
                context.SaveChanges();
            }

            var client = appFactory.CreateClient(new WebApplicationFactoryClientOptions
            {
                AllowAutoRedirect = false
            });

            var result = await client.GetAsync("api/Patients")
                .ConfigureAwait(false);
            var responseContent = await result.Content.ReadAsStringAsync()
                .ConfigureAwait(false);
            var response = JsonConvert.DeserializeObject<Response<IEnumerable<PatientDto>>>(responseContent).Data;

            // Assert
            result.StatusCode.Should().Be(200);
            response.Should().ContainEquivalentOf(fakePatientOne, options =>
                options.ExcludingMissingMembers());
            response.Should().ContainEquivalentOf(fakePatientTwo, options =>
                options.ExcludingMissingMembers());
        }
    } 

【问题讨论】:

    标签: c# asp.net-core asp.net-web-api .net-core xunit


    【解决方案1】:

    嘿,当我在寻找相同的答案时,我看到了你的帖子。我通过将以下代码放入 WebApplicationFactory 的 ConfigureWebHost 方法中解决了这个问题:

        protected override void ConfigureWebHost(
            IWebHostBuilder builder)
        {
            builder.ConfigureServices(serviceCollection =>
            {
    
            });
       
            // Overwrite registrations from Startup.cs
            builder.ConfigureTestServices(serviceCollection =>
            {
                var authenticationBuilder = serviceCollection.AddAuthentication();
                authenticationBuilder.Services.Configure<AuthenticationOptions>(o =>
                {
                    o.SchemeMap.Clear();
                    ((IList<AuthenticationSchemeBuilder>) o.Schemes).Clear();
                });
            });
        }
    

    我知道我迟到了四个月,但我希望你仍然可以使用它。

    【讨论】:

    猜你喜欢
    • 2018-04-15
    • 2022-10-12
    • 2019-06-22
    • 1970-01-01
    • 2021-07-03
    • 2018-07-10
    • 2016-12-11
    • 1970-01-01
    • 2018-08-29
    相关资源
    最近更新 更多