使用 WebApplicationFactory 时如何使用 JWT Bearer Scheme
Posted
技术标签:
【中文标题】使用 WebApplicationFactory 时如何使用 JWT Bearer Scheme【英文标题】:How use JWT Bearer Scheme when working with WebApplicationFactory 【发布时间】: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());
【问题讨论】:
【参考方案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();
);
);
我知道我迟到了四个月,但我希望你仍然可以使用它。
【讨论】:
我也使用这个库进行虚假身份验证:github.com/webmotions/fake-authentication-jwtbearer。也许对你也有一些用处。 我和@weretiger做了同样的事情以上是关于使用 WebApplicationFactory 时如何使用 JWT Bearer Scheme的主要内容,如果未能解决你的问题,请参考以下文章
使用 WebApplicationFactory 时如何使用 JWT Bearer Scheme
学习 ASP.NET Core 2.1:集成测试中使用 WebApplicationFactory
如何在 .net6 中使用 WebApplicationFactory(没有可说的入口点)
AspNetCore 集成测试多个 WebApplicationFactory 实例?